C 異常的幕後8 兩階段處理

2021-09-16 13:09:09 字數 1446 閱讀 7760

nicolasbrailo 

上一章以新增乙個_unwind_能夠呼叫的personality函式而結束。它沒做什麼,但它在那裡。我們已經實現的abi現在可以丟擲異常,捕捉也已經完成一半,但需要正確選擇catch塊(著陸墊)的personality函式目前有點傻。我們通過嘗試理解personality函式接受什麼引數開始新的一章,下次我們將向__gxx_personality_v0新增某些真實的行為:在呼叫__gxx_personality_v0時,我們應該說「是的,這個棧幀確實可以處理這個異常」。

我們已經說過我們不在乎我們小abi的版本或exceptionclass。現在讓我們也忽略上下文:我們將僅處理丟擲函式上的第乙個棧幀;注意這意味著緊靠著丟擲函式之上的函式必須有乙個try/catch塊,否則一切都被打破。這也意味著這個catch將忽略其異常規範,有效地將它變成乙個catch(…)。我們如何讓_unwind_知道我們希望處理當前的異常?

_unwind_reason_code是personality函式的返回值;這告訴_unwind_我們是否找到乙個著陸墊來處理該異常。讓我們實現我們的personality函式返回_urc_handler_found,然後看會發生什麼:

1

2

3

4

5

alloc ex 1

__cxa_throw called

personality function ftw

personality function ftw

no one handled __cxa_throw, terminate!

看到了嗎?我們告訴_unwind_我們找到了乙個處理控制代碼,它再次呼叫personality函式!那裡發生了什麼?

記得活動引數嗎?這是_unwind告訴我們它期望什麼的方式,這是因為異常捕捉分兩階段處理:查詢與清理(或者_ua_search_phase與_ua_cleanup_phase)。讓我們再次重溫異常丟擲與捕捉的方法:

如果沒有找到著陸墊,呼叫預設異常處理控制代碼(通常是std::terminate)。

如果找到乙個著陸墊:

這裡有兩個重要的資訊需要注意:

執行兩階段異常處理過程意味著在沒有找到控制代碼時,預設異常處理控制代碼可以獲得最初異常的棧追蹤(如果我們一邊處理一邊回滾棧,將得不到棧追蹤,或者我們需要持有它的乙個拷貝)!

執行_ua_cleanup_phase並第二次呼叫每個幀,即使我們已經知道這個幀將處理這個異常,也是重要的:personality函式將利用這個機會為在這個作用域上構建的物件執行所有的析構函式。正是這使得raii成為乙個異常安全的習慣用法。

現在我們理解了catch查詢階段如何工作,我們可以繼續實現我們的personality函式。

兩階段提交協議的異常處理

詳見 兩階段提交的協議大家都比較熟悉了,解釋一下每個階段的異常處理。首先,我們需要持久化協議過程中的狀態,這樣如果server宕機,那麼恢復的時候還能通過日誌知道宕機前處於那個階段。同時,所有對資料的修改都會先寫write ahead log,保證宕機重啟的之後資料也不會丟失。寫日誌的順序假定為 寫...

兩階段提交協議的異常處理

兩階段提交的協議大家都比較熟悉了,解釋一下每個階段的異常處理。首先,我們需要持久化協議過程中的狀態,這樣如果server宕機,那麼恢復的時候還能通過日誌知道宕機前處於那個階段。同時,所有對資料的修改都會先寫write ahead log,保證宕機重啟的之後資料也不會丟失。寫日誌的順序假定為 寫wri...

MySQL 兩階段事務的作用

有如下表 select from tt id value 1 1 有如下方法 transactional rollbackfor exception.class isolation isolation.read committed public intincreandget int id updat...