exception safety

整理一下David Abrahams提出的exception safety幾個exception handling level

這在設計function時抱持這個概念非常重要, 因為對使用function的使用者來說, 常常在使用C++的function時,會擔心function失敗時的行為。

使用者角度處理exception handling時,不確定該function到底可能會丟出哪些exception,而造成困擾。

因為可能發生的exception不單是看呼叫的function本身, 該function內部呼叫的函式有可能也會產生exception。 如果真的不確定function可能會丟出哪些exception,最好的方式就是將exception往外傳。

當exception往外傳時,代表function流程中斷了,因此這時候就需要思考function exception safety gurantee

他的概念有點類似資料庫transaction integrity,當transcation failure, 資料庫內容的一致性要達到什麼程度。例如ACID裡的C – consistency類似exception safety的Basic exception safety(invariants在function前後保持一致)

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct.
參考: https://en.wikipedia.org/wiki/ACID#Consistency

以下參考ˋ整理自 https://en.wikipedia.org/wiki/Exception_safety,並加上補註。同時用一個程式安裝包為例子來描述說明exception safety level概念。

這裡安裝包範例假設是一個如同wix bootstrapper安裝包程式或像是visual studio的安裝包包含了許多component。

exception safety level分成以下四個:

No-throw guarantee, also known as failure transparency:
Operations are guaranteed to succeed and satisfy all requirements even in exceptional situations. If an exception occurs, it will be handled internally and not observed by clients.

這代表該function可以直接handle exception, 並且解決即使在exception的情況下,仍然可以正確地完成要做的事。 function是否能達到no throw guarantee要看function本身的spec, 例如要insert data這種的function,如果memory不足導致std::bad_alloc,那insert肯定是做不下去,也沒有其他處理方式。但是如果function的功能是insert data to cache操作,insert發生memory不足,那直接清掉cache再insert就可以silently succeed完成。所以能否達到no-throw要看function spec。

用安裝包的例子就是當磁碟空間不足時,發生exception,基本上無法處理。

Strong exception safety, also known as commit or rollback semantics:
Operations can fail, but failed operations are guaranteed to have no side effects, leaving the original values intact.

這裡的strong exception safety要求沒有side effect,也就是如果function發生exception,要能夠沒有side effect,狀態回復到function call之前。

用安裝包的例子就是當發生exception(例如磁碟空間不足)就將安裝的component解安裝,大多數的安裝包系統都有rollback處理機制。

理論上C++ function在使用時,可以知道會throw哪些exception,但實際上很少這樣做,文件通常都沒有標註,若我們看不到實現細節時,也無從知道到底function內所使用的其他函式會丟出哪些exceptions。如果僅僅要求basic exception safety這通常不會有太大困擾。

若想要達到no-throw或是strong exception safety就意味著需要明確/精確知道底層會丟出哪些exception,並且確定底層function call的exception level

Basic exception safety, also known as a no-leak guarantee:
Partial execution of failed operations can result in side effects, but all invariants are preserved and there are no resource leaks (including memory leaks). Any stored data will contain valid values which may differ from the original values.

這個大概是設計function/class最基本的要求,因為no exception safety有可能導致resource leak,這是在一般設計不希望發生的。 Basic exception safety要求要保證invariant的正確性 (不能有resource leak其實也是一種invariant: 可以想成在single execution flow下呼叫function前後,resource的狀態是一致的)。但是他不保證沒有side effects,也就是function可能事情做到一半,改變了某些值。

用安裝包的例子就是當發生exception就停止繼續安裝,但是確保已安裝的component資訊都是正確完整的(例如windows installer registry)。所以這時候side effect就是那些已成功安裝的component。 並且可能安裝程式為了避免其他安裝程式同時執行會有mutex機制,該mutex要正確release (例如windows MSI會設置 Global_MSIExecute named mutex避免兩個msi同時執行安裝)

No exception safety: No guarantees are made.

這個是最糟糕的,代表如果function拋出了exception,有可能導致invariant失效,resource leak(例如在function裡new但是沒有delete)。一般情況,如果有follow RAII,可以做到basic exception safety。

用安裝包的例子就是當發生exception不保證任何事情,有可能安裝完成的component registry資訊寫一半。或是installer mutex沒有解除,導致其他安裝程式也無法進行安裝。

This entry was posted in C++ idioms. Bookmark the permalink.

Leave a Reply