You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/* Redis generally does not try to recover from out * of memory conditions when allocating objects or * strings, it is not clear if it will be possible * to report this condition to the client since the * networking layer itself is based on heap * allocation for send buffers, so we simply abort. * At least the code will be simpler to read... */staticvoidoom(constchar*msg) {
fprintf(stderr, "%s: Out of memory\n",msg);
fflush(stderr);
sleep(1);
abort();
}
C requires significant administrative code since it doesn't support exceptions, try-finally blocks, or RAII at all. A typical approach is to separate the releasing of resources at the end of the function and jump there with gotos in the case of error. This way the cleanup code need not be duplicated.
直接看一個複雜的例子:
intfoo(intbar)
{
intreturn_value=0;
allocate_resources_1();
if (!do_something(bar))
goto error_1;
allocate_resources_2();
if (!init_stuff(bar))
goto error_2;
allocate_resources_3();
if (!prepare_stuff(bar))
goto error_3;
return_value=do_the_thing(bar);
error_3:
cleanup_3();
error_2:
cleanup_2();
error_1:
cleanup_1();
returnreturn_value;
}
拔掉 goto 後:
intfoo(intbar)
{
intreturn_value=0;
allocate_resources_1();
if (do_something(bar))
{
allocate_resources_2();
if (init_stuff(bar))
{
allocate_resources_3();
if (prepare_stuff(bar))
{
return_value=do_the_thing(bar);
}
cleanup_3();
}
cleanup_2();
}
cleanup_1();
returnreturn_value;
}
Table Of Contents
Handling out-of-memory conditions in C
The policies
Recovery
最不常見的做法,因為 recovery 不好做,且不同領域的作法大相徑庭。Recovery 的動作可能由下列可能組成:
Abort
最常用的方法,印出錯誤訊息後終止。以
gnulib
為例,裡面的xmalloc
設計:Segfault
沒什麼好講的,就是一個最簡單的方法。
Example
接著 Eli 大師挑了幾個函式庫跟應用程式來探討他們的處理方式,這邊列幾個比較有趣的:
SQLite (recovery policy)
SQLite 提供多個選擇給使用者:
這邊是一般的 malloc-like 方案:
有趣的地方是,他會多要一塊空間來紀錄這塊 chunk 的大小(並非實際 heap chunk 的大小,而是使用者要求的大小),來讓分配者可以簡單地直接回報 chunk 大小。
Git (recovery policy)
Redis (abort policy)
Redis 自己實作了一個
zmalloc
但是當malloc
回傳是NULL
時他不會自動結束,而是照舊回傳。Redis 內部的模組都會將zmalloc
傳遞到使用層,若使用層發現有NULL
就直接呼叫下面的oom
函式。這裡的註解很清楚地描述了為什麼在應用程式中使用 abort policy 通常是最符合邏輯的。
總結
一個好的函式庫不應該會揭露自己的實作風格 (idiosyncrasy) 給呼叫者,因此一般建議使用 recovery 方式。同時也可以提供一些方法讓使用者自己定義處理的方式,增加彈性。
以應用程式來說,Eli 大師認為 abort 方式是最好的。寫一個 wrapper 把 abort 邏輯包裝起來可以讓整個程式碼的邏輯更清晰,也省下很多麻煩;同時,未來需要修改方式時也很簡單,直接去修改 wrapper。
Using goto for error handling in C
錯誤處理
錯誤處理經常伴隨著資源的釋放處理,而這常常很令人頭痛。(後面有🌰)不過在 C++ 採用的 RAII (Resource Acquisition Is Initialization) 設計下,這件事可以很漂亮的被解決。這邊討論的是在 C 語言中要怎麼手動處理這樣的情況。
在 RAII wiki 中有這樣的一句話:
直接看一個複雜的例子:
拔掉
goto
後:整個函式的行為主體在不使用
goto
的情況下被塞進了很深層的巢狀條件中,相較上面使用goto
的程式碼不使用goto
造成整體的可讀性是比較低的。最後 Eli 大師也提到上面的例子還算簡單,在 Linux kernel 中有更複雜的例子。這邊提供另一個更複雜的例子 - RAII in C。
額外的使用情境
除了錯誤處理外,有些時候多層的迴圈也會需要直接跳離所有迴圈,這時候
goto
也十分適合,相較之下若不使用goto
就必須要在每個迴圈的條件判斷式中加入某些額外的判斷,不但增加複雜度也讓可維護性降低。The text was updated successfully, but these errors were encountered: