Usage of 'goto' #205
-
Hey, I was just looking around, and found various // ...
if (OS_VERIFY_SMBIOS3(pMappedAddr))
{
status = NV_OK;
goto done;
}
if (OS_VERIFY_SMBIOS(pMappedAddr))
{
status = NV_OK;
}
done:
os_unmap_kernel_space(pBufAddr, size);
return status;
// ... instead of this: // ...
if (OS_VERIFY_SMBIOS3(pMappedAddr))
{
status = NV_OK;
s_unmap_kernel_space(pBufAddr, size);
return status;
}
if (OS_VERIFY_SMBIOS(pMappedAddr))
{
status = NV_OK;
}
// ... Example taken from os.c I understand that in controlled cases it won't do anything, but still.. also subjectively, it messes up readability of the code. EDIT: In many cases it enforces "DNRY", and I imagine it would be pain to implement as a function.. but there are some cases where the goto is used only once in the function, or even entire file |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Thank you for the interest in our codebase.
Generally the
Our rough guidelines here say to use regular There is however a difference in how it is used in nvidia.ko versus what e.g. linux kernel does. Consider this artificial example using kernel style: void *get_some_resource() {
A *a;
B *b;
C *c;
a = allocate_a();
if (!a)
return ERR_PTR(-ENOMEM);
b = allocate_b(a);
if (!b)
goto err1;
c = allocate_c(b);
if (!c)
goto err2;
return c;
err2:
free_b(b);
err1:
free_a(a);
return ERR_PTR(-ENOMEM);
} A more idiomatic way to write this in nvidia.ko (other modules have slightly different conventions) would be: NV_STATUS get_some_resource(void **res)
{
NV_STATUS status;
A *a = NULL;
B *b = NULL;
C *c = NULL;
status = allocate_a(&a);
if (status != NV_OK)
goto done;
status = allocate_b(&b, a);
if (status != NV_OK)
goto done;
status = allocate_c(&c, b);
if (status != NV_OK)
goto done;
*res = c;
done:
if (status != NV_OK)
{
free_b(b); // these are typically NOPs when NULL is passed in
free_a(a);
}
return status;
} Or with some error-checking macros: NV_STATUS get_some_resource(void **res)
{
NV_STATUS status;
A *a = NULL;
B *b = NULL;
C *c = NULL;
NV_ASSERT_OK_OR_GOTO(status, allocate_a(&a), done);
NV_ASSERT_OK_OR_GOTO(status, allocate_b(&b, a), done);
NV_ASSERT_OK_OR_GOTO(status, allocate_c(&c, b), done);
*res = c;
done:
if (status != NV_OK)
{
free_b(b);
free_a(a);
}
return status;
} Notably, we usually only have one label (often called There are of course many exceptions to this in the codebase. Guidelines can be ignored whenever there is a strong reason to do so. |
Beta Was this translation helpful? Give feedback.
Thank you for the interest in our codebase.
goto
is a tool of the language like any other. Knives are sharper than forks, so you ought to be more careful when using them, but you're gonna have a bad time eliminating all knife use when making food, even if technically doable.Generally the
goto cleanup;
idiom is used a lot in C code, as it is the safest way to ensure all cleanup is performed on every function exit path. It's not just a matter of minimizing duplicated lines either. When adding the cleanup calls (s_unmap_kernel_space()
in your example) directly before every return, you have two common failure modes: