今回のゴールはSymbian OSにおけるメモリ管理(リソース管理)フレームワークです。このフレームワークの必要性を明らかにするために、メモリ管理(リソース管理)で解決を図ろうとした根源の問題までさかのぼってみましょう。
Cで関数呼び出しをする際の典型的なシーケンスを考えてみます。
これを縦軸に時間軸、横軸にコーリングシーケンスを取って配置したものを図3に示します。
何が問題なのかと思われるかもしれません。が、途中の関数が戻り値判定と、それを引き継いで上位にエラーコードを返す処理を怠った場合を考えてみましょう。すると当たり前ですが、上位の関数は不適切な状態のまま処理を継続することになります。要するに不具合の発生です。
プログラムの不具合や、APIの戻り値に対する誤解や、そのほか不思議な事情により戻り値判定が漏れる可能性が5%あるとすると、ある関数が正しく動作する可能性はその残り95%になります。関数呼び出しは直列システムですから、全体の信頼性はこれらの信頼性の積として求まります。関数呼び出しのネストが10段あるとすると、0.95の10乗となりますから、この場合全体の信頼性は約60%まで低下してしまいます。
プロジェクトの参加者全員が必ず戻り値判定を適切に行ってくれれば問題ない、といい張る人もいますが本当でしょうか。筆者はこのようなナイーブな態度を「希望的観測に基づくプログラミング」とか「善意に基づくプログラミング」などと呼んでいます。
障害などの結果を受けてある処理をリトライしようとした場合、通常リトライ制御はユーザーが認識している文脈に近い個所で行う必要があります。トランザクションを破棄してもう1回やり直すなどという処理を、エラーを最初に発見する一番深いレベルの関数に行わせるわけにはいきません。何としてもトランザクションを認識できるトップレベルまで戻ってから、リトライの要否、可否を判定して、処理を継続しなければ。そのためにも必要なエラーを伝えるべきところに確実に伝える仕掛けが必要です。
これらの問題点を解決するため、現代のプログラミング言語では例外事象(各種障害の検出、サービス範囲外の状態の検出な)を確実に通知するための機構、例外処理というものを支援するのが一般的です。例外処理は事象を通知する側と、それを受ける側の二者から構成されます。
C++で用意されている構文では、通知側はthrow、受け側はcatchとなっています。ポイントを示します。
図3のコーリングシーケンスに、この例外処理を導入したものが図4です。
ただしSymbian OSではこの例外処理機能の提供方法が、C++標準とは微妙に異なっています。具体的にはthrowに相当するのがUser::LeaveというAPI、catchに相当するのがTRAPというマクロで提供されています。これはSymbian OSのデザインが行われていたとき、C++には例外処理の機能が標準では入っていなかったことに依ります。
永らくこれらの内側にはSymbian OSのフレームワークが支援する独自の例外処理機構が潜んでいました。しかしSymbian OSのv9.1からはthrow−catchベースの実装に変わっています。ではUser::Leave−TRAPはどうなるのですか、という質問を受けることがありますが、APIとしてのUser::Leave−TRAPがなくなるわけではありません。ご安心ください。むしろthrow−catchとの混在について明確なルールができるまで、当面の間はUser::Leave−TRAPベースのコードを墨守すべきです。
例外に関する注意事項を補足しておきます。
間違っても現状の関数リターンで返していたエラー情報を、すべて例外に置き換えようなどとは思わないように。
L付きルールの適用を忘れた場合、実際の開発プロジェクトでは非常に厳しい指導が行われるはずです。
さてエラー処理に関する問題の何割かがこのようにシステムレベルで解決されることになりました。しかしメモリ管理にとってはこれこそが問題の始まりだったのです。
Copyright © ITmedia, Inc. All Rights Reserved.