View on C++ exceptions in kernel space
View on C++ exceptions in kernel space
As the title states, what is the general view on C++ exceptions in kernel space?
Obviously it comes at a high cost in terms of having to implement (or port) a full unwinding library, also I guess in terms of performance (one can imagine caches being trashed and such).
However would it be worth trading OS size and performance for cleaner, and possibly more readable code? - Also in the case that the performance hit is too bad, wouldn't it make sense to use a hybrid scheme (C return code exceptions for code that requires high-performance, and C++ exceptions for the rest of the kernel).
I am aware that some people want to squeeze every little drop of performance out of their system, however if clean code and readability is of larger importance, wouldn't it make sense to utilize a feature such as C++ exceptions?
Also, just curious, as I'm somewhat new to OS development, how bad is it to have a large kernel, and when is a kernel considered large? (I know the last question is undoubtedly arguable, but I'm here to get peoples view, not a 'correct' answer)
Obviously it comes at a high cost in terms of having to implement (or port) a full unwinding library, also I guess in terms of performance (one can imagine caches being trashed and such).
However would it be worth trading OS size and performance for cleaner, and possibly more readable code? - Also in the case that the performance hit is too bad, wouldn't it make sense to use a hybrid scheme (C return code exceptions for code that requires high-performance, and C++ exceptions for the rest of the kernel).
I am aware that some people want to squeeze every little drop of performance out of their system, however if clean code and readability is of larger importance, wouldn't it make sense to utilize a feature such as C++ exceptions?
Also, just curious, as I'm somewhat new to OS development, how bad is it to have a large kernel, and when is a kernel considered large? (I know the last question is undoubtedly arguable, but I'm here to get peoples view, not a 'correct' answer)
// Skeen
// Developing a yet unnamed microkernel in C++14.
// Developing a yet unnamed microkernel in C++14.
Re: View on C++ exceptions in kernel space
Hi,
Is cleaner than:
What makes you think code would be more readable and easier to maintain if a function you call can unexpectedly "goto" a random place somewhere else?
Let's rephrase your question: "would it be worth trading OS size and performance for uglier and harder to read/maintain code?"
The answer seems quite obvious to me..
Cheers,
Brendan
What makes you think that this:skeen wrote:However would it be worth trading OS size and performance for cleaner, and possibly more readable code?
Code: Select all
try {
foo();
} catch(int e) {
return ERR_BORKED;
}
Code: Select all
if( (status = foo()) != OK) return status;
Let's rephrase your question: "would it be worth trading OS size and performance for uglier and harder to read/maintain code?"
The answer seems quite obvious to me..
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: View on C++ exceptions in kernel space
And when the code grows, wouldn't it be easier to have one try-catch block instead of a dozen if (error) {cleanup; forward_error;} statements, and no chance you accidentally missed one?
Syntactic sugar is all in the eye of the beholder
Syntactic sugar is all in the eye of the beholder
Re: View on C++ exceptions in kernel space
If you think C++ exceptions are just another way of returning an error-code, you have misunderstood the concept. The idea is that the exception handler should fixup the state of the code (for instance, free allocated memory and other resources), and then gracefully exit the code block (and not with return).Brendan wrote:Hi,
What makes you think that this:skeen wrote:However would it be worth trading OS size and performance for cleaner, and possibly more readable code?
Is cleaner than:Code: Select all
try { foo(); } catch(int e) { return ERR_BORKED; }
Code: Select all
if( (status = foo()) != OK) return status;
Re: View on C++ exceptions in kernel space
I agree. I also wonder how Brendan's main procedure would handle specific error-codes generated by the sound card-driver 10 levels below? It would need a switch statement with all possible error-codes in the whole OS.Combuster wrote:And when the code grows, wouldn't it be easier to have one try-catch block instead of a dozen if (error) {cleanup; forward_error;} statements, and no chance you accidentally missed one?
Syntactic sugar is all in the eye of the beholder
-
- Member
- Posts: 595
- Joined: Mon Jul 05, 2010 4:15 pm
Re: View on C++ exceptions in kernel space
The problem isn't that exceptions are bad or slow or anything, it is that for GCC at least it is a part of the libstdc++. In order to include libstdc++ you must implement or stub several POSIX functions. As soon you link with libstdc++, the binary will grow 300k as libstdc++ must be statically linked. Also, there is a risk that the exception code might allocate memory dynamically, something that not might be ready during boot. There are simply many pitfalls with exceptions when including them in a kernel.
Re: View on C++ exceptions in kernel space
Hi,
The point here is that it's a kernel. The interfaces/API should be well designed (including a standardised set of error codes); and (except for often complex/tricky cleanup), it doesn't handle any errors and simply returns an error code to user-space.
Cheers,
Brendan
Are you suggesting that 12 catch blocks are cleaner; or are you implying that a kernel doesn't need to handle different error conditions from different places in different ways (and do various parts of cleanup in a specific order - e.g. releasing locks)?Combuster wrote:And when the code grows, wouldn't it be easier to have one try-catch block instead of a dozen if (error) {cleanup; forward_error;} statements, and no chance you accidentally missed one?
[Sarcasm] Yes! For example, if the user is playing chess and tries to make an invalid move, my micro-kernel has to handle that error. [/Sarcasm]rdos wrote:I agree. I also wonder how Brendan's main procedure would handle specific error-codes generated by the sound card-driver 10 levels below? It would need a switch statement with all possible error-codes in the whole OS.
The point here is that it's a kernel. The interfaces/API should be well designed (including a standardised set of error codes); and (except for often complex/tricky cleanup), it doesn't handle any errors and simply returns an error code to user-space.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re: View on C++ exceptions in kernel space
Code: Select all
buffer1 = kmalloc(BUFFER1_SIZE);
if (buffer1 == NULL) {
return SOME_ERROR;
}
hardware = GetHardWare();
if (hardware == NULL) {
free(buffer1);
return SOME_OTHER_ERROR;
}
buffer2 = kmalloc(BUFFER2_SIZE);
if (buffer2 == NULL) {
free(buffer1);
return SOME_ERROR;
}
buffer3 = kmalloc(BUFFER3_SIZE);
if (buffer3 == NULL) {
free(buffer1);
free(buffer2);
return SOME_ERROR;
}
Code: Select all
try {
buffer1 = kmalloc(BUFFER1_SIZE);
} catch(int e) {
return SOME_ERROR;
}
try {
hardware = GetHardWare();
} catch(int e) {
free(buffer1);
return SOME_OTHER_ERROR;
}
try {
buffer2 = kmalloc(BUFFER2_SIZE);
} catch(int e) {
free(buffer1);
return SOME_ERROR;
}
try {
buffer3 = kmalloc(BUFFER3_SIZE);
} catch(int e) {
free(buffer1);
free(buffer2);
return SOME_ERROR;
}
Maybe something like this would be better in this case:
Code: Select all
buffer1 = NULL;
buffer2 = NULL;
buffer3 = NULL;
try {
buffer1 = kmalloc(BUFFER1_SIZE);
hardware = GetHardWare();
buffer2 = kmalloc(BUFFER2_SIZE);
buffer3 = kmalloc(BUFFER3_SIZE);
} catch(int e) {
if (buffer1 != NULL) free(buffer1);
if (buffer2 != NULL) free(buffer2);
if (buffer3 != NULL) free(buffer3);
return SOME_ERROR;
}
Re: View on C++ exceptions in kernel space
Exceptions should always be handled in the function that causes them, or the possibility of throwing an exception should be well documented and handled by the calling function. Exceptions are nice for accessing parameters passed from user mode which might reside at invalid addresses. Otherwise I doubt that I would use them for much.
Re: View on C++ exceptions in kernel space
Hi,
I look forward to seeing the functionally equivalent "try-catch" version.
Cheers,
Brendan
Not really. Try something more realistic:Antti wrote:Did I understand correctly?
Code: Select all
int KERNEL_API_allocatePage(void *address) {
int type;
uint64_t phys_page;
int status;
if(address > something) return ERR_DENIED; // User code can't allocate pages in kernel-space
retry:
acquire_lock( virtual_address_space->lock ); // In case other threads are trying to alloc the same page
type = get_virtual_page_type(address);
if(type != NOT_PRESENT) {
release_lock( virtual_address_space->lock );
return ERR_EXISTS;
}
status = alloc_physical_page(&phys_page);
if(status == ERR_NOMEM) {
release_lock( virtual_address_space->lock );
block_task_until_memory_available();
goto retry;
} else if(status != OK) { // Quota exceeded?
release_lock( virtual_address_space->lock );
return status;
}
status = map_physical_page(address, phys_page);
if(status == ERR_NOMEM) { // Failed to allocate physical page for page table or something
release_lock( virtual_address_space->lock );
free_physical_page(phys_page);
block_task_until_memory_available();
goto retry;
}
if(status != OK) {
free_physical_page(phys_page);
}
return status;
}
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: View on C++ exceptions in kernel space
Done. Of course, in a real kernel map_physical_page would throw the PageExistsError
Code: Select all
int KERNEL_API_allocatePage(void *address) {
if(address > something)
throw AccesDenied(); // User code can't allocate pages in kernel-space
for(;;) {
Locked _(virtual_address_space->lock);
if(get_virtual_page_type(address) != NOT_PRESENT) {
throw PageExistsError(address);
}
try {
// alloc_physical_page throws std::bad_alloc on failure
// PhysicalPageRef's destructor releases the page if it still holds it
PhysicalPageRef phys_page(alloc_physical_page());
// PhysicalPageRef&& overload of map_physical_page frees the page on
// error.
map_physical_page(address, std::move(phys_page));
return;
} catch(std::bad_alloc& e) {
block_task_until_memory_available();
continue;
}
}
}
Re: View on C++ exceptions in kernel space
Mostly personal preference, but also the fact that you can actually propagate more information, than a single error-code out of the function, also I would never catch an exception just to throw the error-code, if the function receiving the exception is unable to handle it, the straight forward thing to do is to simply let it pass through to someone who can actually handle it.Brendan wrote:What makes you think that this:skeen wrote:However would it be worth trading OS size and performance for cleaner, and possibly more readable code?
Is cleaner than:Code: Select all
try { foo(); } catch(int e) { return ERR_BORKED; }
Code: Select all
if( (status = foo()) != OK) return status;
Also I like the possibility of having hierarchy in my errors, so that I can catch more than one in a single catch statement. And then there's the issue, that if your function calls multiple sub-functions that returns errors-codes, that you cannot handle, then you'll have to return their errors to your callee (possibly the union of the functions your calling, and you may have to remap several of them in case there's an overlap), which to me seems silly.
As said before I might want a 'random' goto, to somewhere that can actually handle the issue, which seems reasonable to me.Brendan wrote: What makes you think code would be more readable and easier to maintain if a function you call can unexpectedly "goto" a random place somewhere else?
// Skeen
// Developing a yet unnamed microkernel in C++14.
// Developing a yet unnamed microkernel in C++14.
Re: View on C++ exceptions in kernel space
A little addition to my previous post: there is a little chance that the sarcasm in this statement was not obvious. Even in my little simple (not realistic, though) example I do not find exceptions any better than the traditional error handling. It resembled the same code flow structure anyway and try-catches look harder to read (for me). What about all the assembly procedures? I do not even know how I make them to throw an exception.Antti wrote:Exceptions are much cleaner, more elegant, easy to read, and make everything a lot easier?
Let alone all the overhead it makes to have them at all. Even if that is not the issue, I do not where I would use exceptions. But that also because I am not expert. Sorry for disturbing this thread.
Re: View on C++ exceptions in kernel space
According to C++ Exception Support all you need to port is libcxxrt and libgcc_eh, which doesn't seem that massive, also I must redirect you to my first question;OSwhatever wrote:The problem isn't that exceptions are bad or slow or anything, it is that for GCC at least it is a part of the libstdc++. In order to include libstdc++ you must implement or stub several POSIX functions. As soon you link with libstdc++, the binary will grow 300k as libstdc++ must be statically linked. Also, there is a risk that the exception code might allocate memory dynamically, something that not might be ready during boot. There are simply many pitfalls with exceptions when including them in a kernel.
And as for the allocation, couldn't one just provide a static-memory pool, which can at least hold the most common exceptions?Skeen wrote: ... How bad is it to have a large kernel, and when is a kernel considered large? (I know the last question is undoubtedly arguable, but I'm here to get peoples view, not a 'correct' answer)
If there are any other pitfalls, please let me know! I'm not trying to shoot you down, I'm just trying to reach a conclusion as to whether I should use C++ exceptions for my kernel or not.
// Skeen
// Developing a yet unnamed microkernel in C++14.
// Developing a yet unnamed microkernel in C++14.
Re: View on C++ exceptions in kernel space
Typical C++ exception handling does the cleanup locally, and does not delegate the responsibility for this to the caller (via error codes).Brendan wrote: Are you suggesting that 12 catch blocks are cleaner; or are you implying that a kernel doesn't need to handle different error conditions from different places in different ways (and do various parts of cleanup in a specific order - e.g. releasing locks)?
Not so. If a user is playing chess, and your audio-driver misses to update the sound stream, the sound driver would return error-code "MISSED_FRAME", which the rest of the OS just passes to the caller, leaving the responsibility for the chess application to handle. Additionally, the chess application uses a MPEG renderer library which doesn't care to handle the error code either, but rather expects the chess application to know best what to do with it.Brendan wrote: [Sarcasm] Yes! For example, if the user is playing chess and tries to make an invalid move, my micro-kernel has to handle that error. [/Sarcasm]