×
Namespaces

Variants
Actions

Как перехватить панику

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

Статья
Перевод:
Оригинал: How to catch a panic
Den123
Последнее редактирование: hamishwillee (09 Dec 2011)

Строго говоря, паники не предназначены для того чтобы их перехватывали, однако, в некоторых случаях это необходимо. Для начала выясним, что это за случаи, в чем смысл. Обычно паники используются для обозначения программных ошибок которые должны быть уже исправлены и, в собранной для распространения версии программы, не должны встречаться. Паника всегда завершает выполнение программы так как программа входит в состояние из которого не может корректно продолжить свое выполнение. Для чего же перехватывать паники, если это вообще возможно ?

Есть по крайней мере одна специфическая причина - автоматизированное тестирование программы. Предположим, для проведения тестирования используется определенный набор тестов которые выполняются последовательно. Тогда, если во время выполнения одного из них возникнет паника, основной процесс, который выполняет тестирование будет прерван и оставшиеся тесты не будут выполнены. Поэтому, для того чтобы тестирование всегда проходило корректно и доходило до конца, необходимо перехватывать паники.

Можно ли перехватить панику ? На первый взгляд это можно сделать с помощью вызова User::SetExceptionHandler(). Но это утверждение ошибочно: исключения и паники - разные вещи. Поэтому установка нового обработчика исключения не позволит перехватывать ВСЕ паники, разве что только некоторые (например KERN-EXEC 3).

Общее решение проблемы - выполнение тестов на отдельной нити. Отдельная нить находится под вашим полным контролем: Вы можете создавать, уничтожать и, что важно, мониторить ее состояние. Вы можете подписаться на оповещение в случае если нить будет уничтожена обычным способом или воспользоваться вызовом RThread::Logon(). Кроме того, используя метод RThread::ExitReason() Вы можете получить информацию о том как и когда нить завершила свое выполнение. Даже в случае возникновения паники в одном из тестов, основная нить вашего тестирующего приложения может нормально продолжить свою работу.

Ниже приведен пример консольного приложения(exe) которое позволяет мониторить уничтожение другой нити, выводит тип и причину:


#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


#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;
}
This page was last modified on 9 December 2011, at 02:33.
65 page views in the last 30 days.
×