收藏
How to catch a panic
http://wiki.forum.nokia.com/index.php/How_to_catch_a_panic
http://wiki.forum.nokia.com/index.php/%E5%A6%82%E4%BD%95%E6%8D%95%E6%8D%89panic
From Forum Nokia Wiki
Yes, a panic. Although panics, by their nature, are uncatchable there is a way to prevent them from severely influencing your program.
First off, why would you do it? What is the use-case? Typically a panic draws attention to a programmatic error that should have already been corrected in the program. It always terminates the program execution and it's on purpose: programs panic from an unrecoverable stage. So why would we want to catch a panic if we were able to do so?
Well, there is at least one special use-case: in a test framework used for automated testing. In a typical unit testing framework, for example, where test suites containing test cases are being performed it's always a nice feature if the execution of tests doesn't get interrupted even in case of a fatal error. If the severe programming error doesn't harm other test cases, then it's ideal if the termination of a test case doesn't influence the life-time of other test cases.
Can panics be caught? At first sight, it may seem that User::SetExceptionHandler() would do. But it doesn't: exceptions are not the same as panics. So installing a new exception handler will not enable us to catch panics, even though it makes it possible to catch a small portion of ALL panics (e.g. an access violation aka KERN-EXE 3).
The ultimate solution is to let your test case run in a new thread. A new thread that you have control over: you create, destroy and most importantly monitor it. You can request notification when this thread dies normally or otherwise via RThread::Logon(). Combined this with RThread::ExitReason(), you can get a clue when and how a thread has exited. Even in case of a panic your main thread will not be affected at all so your - test - framework can continue to run.
The following example shows a console based exe which monitors other thread's death, displaying type and reason:
对,说的就是Panic。尽管就其本性来说,panic是“不可捕获的”,但仍有一种途径可以阻止它严重影响你的程序。
首先,为什么你要这样做呢?用况是什么?典型地,panic引起对应该已经在程序中更正的编程错误的注意。panic总是终止程序运行,并是故意这样设计的: 程序从“不可恢复的”运行阶段发生panic。因此,为什么我们需要捕获panic(假定我们能够这样做)?。
好了,至少有一个特殊的用处: 在测试框架中用于自动测试。在一个典型的单元测试框架中,比如,执行含有测试用例的测试集,若即使发生致命错误测试也不中断,这总是一个很好的功能。如果严重的编程错误不伤害其它的测试用例,那么某个测试用例的终止不影响其它用例的生命周期将是很理想的。
panic能被捕获吗?乍看起来,似乎User::SetExceptionHandler()能办到。但它却办不到。异常与panic不同。因此,安装一个新异常处理器不会使我们能捕获到panic,即使它使捕获一小部分异常成为可能(比如,访问违规或者说KERN-EXE 3)。
最终的解决方案: 让你的测试用例'运行在一个新线程中。一个你控制的新线程: 创建、销毁它,最重要的是监视它。经由RThread::Logon()你能够请求获知线程死亡正常与否。
下例演示了基于一个控制台的Exe,检测其他线程的死亡,显示类型及原因:
view plaincopy to clipboardprint?
#ifndef THREADNOTIFIER_H
#define THREADNOTIFIER_H
#include <e32base.h>
class CThreadNotifier : public CActive
{
public:
CThreadNotifier();
~CThreadNotifier();
void ConstructL();
void IssueRequest();
protected:
void RunL();
void DoCancel();
TInt RunError(TInt aError);
RUndertaker iUndertaker;
TInt iThreadHandle;
};
#endif
#ifndef THREADNOTIFIER_H
#define THREADNOTIFIER_H
#include <e32base.h>
class CThreadNotifier : public CActive
{
public:
CThreadNotifier();
~CThreadNotifier();
void ConstructL();
void IssueRequest();
protected:
void RunL();
void DoCancel();
TInt RunError(TInt aError);
RUndertaker iUndertaker;
TInt iThreadHandle;
};
#endif
view plaincopy to clipboardprint?
#include "ThreadNotifier.h"
#include <e32cons.h>
_LIT(KPanicMsg, "THREAD-NOTIFIER");
LOCAL_D CConsoleBase* console;
CThreadNotifier::CThreadNotifier()
: CActive(CActive::EPriorityStandard)
{
CActiveScheduler::Add(this);
}
void CThreadNotifier::ConstructL()
{
User::LeaveIfError(iUndertaker.Create());
}
CThreadNotifier::~CThreadNotifier()
{
Cancel();
iUndertaker.Close();
}
void CThreadNotifier::IssueRequest()
{
__ASSERT_ALWAYS(!IsActive(), User::Panic(KPanicMsg, 0));
iUndertaker.Logon(iStatus, iThreadHandle);
SetActive();
}
void CThreadNotifier::RunL()
{
if (iStatus == KErrDied)
{
RThread thread;
thread.SetHandle(iThreadHandle);
console->Printf(_L("Thread %S (%d) died (Type: %d, reason %d)/n"),
&thread.Name(), (int)thread.Id(), thread.ExitType(), thread.ExitReason());
thread.Close();
}
IssueRequest();
}
void CThreadNotifier::DoCancel()
{
iUndertaker.LogonCancel();
}
TInt CThreadNotifier::RunError(TInt /*aError*/)
{
return KErrNone;
}
LOCAL_C void callExampleL()
{
console = Console::NewL(_L("Thread Notifier"), TSize(KDefaultConsWidth,
KDefaultConsHeight));
CleanupStack::PushL(console);
CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
CThreadNotifier* notifier = new(ELeave) CThreadNotifier;
CleanupStack::PushL(notifier);
notifier->ConstructL();
notifier->IssueRequest();
CActiveScheduler::Start();
CleanupStack::PopAndDestroy(3, console); // console, scheduler, notifier
}
GLDEF_C TInt E32Main()
{
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
TRAPD(error, callExampleL());
__ASSERT_ALWAYS(!error, User::Panic(KPanicMsg, error));
delete cleanup;
__UHEAP_MARKEND;
return 0;
}