📜 ⬆️ ⬇️

About one trick to return an error code from a function

The Linux kernel is a storehouse of the algorithms used, as well as some hacker or semi-hacker tricks designed to speed up and / or reduce memory footprint. I want to tell you about one of these half-knocker tricks.


In our wretched world , the C language as such, the links and classes do not exist, but there are pointers and structures. Often the question is what to choose as the return value of the function, which should return to the caller a pointer to the living object? There are at least two variants of prototypes:

struct obj *get_obj(...); 

and
 void get_obj(..., struct obj**); 


Why did I single out the second method, although it seems to be ineffective here? And the thing is this. Initially, we can return a pointer to an object or NULL, if one was not found. However, any programmer has a question: what about the exceptions inside the method? Yes, in the Wild West, in C language, we use the return code. And here there is such a picture (see the prototypes above and compare):
 struct obj *get_obj(..., int *ret); int get_obj(..., struct obj **); 

')
But now the task is to reduce the number of function arguments, since this greatly affects both the execution speed and the amounts of memory consumed on the stack (we are talking about the OS kernel, right? :-)).

For this was come up with such a solution. Since void * is still a number, let's divide the possible values ​​into three segments (consider the 32-bit case):

The first is a NULL pointer, the second is a normal address space, and the third is an invalid pointer or error code. Accordingly, several macros for checking appeared:
 ZERO_OR_NULL_PTR(); IS_ERR(); IS_ERR_OR_NULL(); 

And people began to use them!

The resulting prototype turned into:
 struct obj *get_obj(...); 

And an example of use:
 struct obj *get_obj(...) { struct obj *obj = malloc(sizeof(*obj)); int ret; if (!obj) return ERR_PTR(-ENOMEM); ret = do_smth(obj, ...); if (ret) { free(obj); return ERR_PTR(ret); } return obj; } int whatever(...) { struct obj *obj = get_obj(...); if (IS_ERR(obj)) return PTR_ERR(obj); printf("Cool object %p\n", obj); return 0; } 

There are also problems with this approach, for example, due to the incorrect use of the IS_ERR_OR_NULL macro for those parts of the code where NULL is a valid return, for example, when the object is responsible for some functionality in the kernel, which is disabled in this kernel assembly. Then returns a NULL pointer that needs to be handled differently!

Source: https://habr.com/ru/post/252501/


All Articles