- •Contents
- •1.1 Fundamental Types
- •1.2 T Classes
- •1.3 C Classes
- •1.4 R Classes
- •1.5 M Classes
- •1.6 Static Classes
- •1.7 Buyer Beware
- •1.8 Summary
- •2 Leaves: Symbian OS Exceptions
- •2.1 Leaving Functions
- •2.3 Constructors and Destructors
- •2.4 Working with Leaving Functions
- •2.5 Trapping a Leave Using TRAP and TRAPD
- •2.6 LeaveScan
- •2.7 Summary
- •3 The Cleanup Stack
- •3.1 Using the Cleanup Stack
- •3.2 How Does the Cleanup Stack Work?
- •3.4 Using TCleanupItem for Customized Cleanup
- •3.5 Portability
- •3.6 An Incidental Note on the Use of Casts
- •3.7 Summary
- •5 Descriptors: Symbian OS Strings
- •5.3 Pointer Descriptors
- •5.4 Stack-Based Buffer Descriptors
- •5.5 Heap-Based Buffer Descriptors
- •5.6 Literal Descriptors
- •5.7 Summary
- •6 Good Descriptor Style
- •6.1 Descriptors as Parameters and Return Types
- •6.2 Common Descriptor Methods
- •6.3 The Use of HBufC Heap Descriptors
- •6.4 Externalizing and Internalizing Descriptors
- •6.5 The Overuse of TFileName
- •6.6 Useful Classes for Descriptor Manipulation
- •6.7 Summary
- •7 Dynamic Arrays and Buffers
- •7.1 CArrayX Classes
- •7.3 Why Use RArray Instead of CArrayX?
- •7.4 Dynamic Descriptor Arrays
- •7.5 Fixed-Length Arrays
- •7.6 Dynamic Buffers
- •7.7 Summary
- •8.1 Multitasking Basics
- •8.2 Event-Driven Multitasking
- •8.3 Working with Active Objects
- •8.4 Example Code
- •8.5 Threads Without an Active Scheduler
- •8.6 Application Code and Active Objects
- •8.7 Summary
- •9 Active Objects under the Hood
- •9.1 Active Object Basics
- •9.2 Responsibilities of an Active Object
- •9.3 Responsibilities of an Asynchronous Service Provider
- •9.4 Responsibilities of the Active Scheduler
- •9.5 Starting the Active Scheduler
- •9.6 Nesting the Active Scheduler
- •9.7 Extending the Active Scheduler
- •9.8 Cancellation
- •9.9 Request Completion
- •9.10 State Machines
- •9.11 Long-Running Tasks
- •9.14 Common Mistakes
- •9.15 Summary
- •10 Symbian OS Threads and Processes
- •10.2 Thread Priorities
- •10.3 Stopping a Running Thread
- •10.5 Exception Handling
- •10.6 Processes
- •10.7 Summary
- •11.2 How Do the Client and Server Fit Together?
- •11.3 How Do the Client and Server Communicate?
- •11.5 How Do Synchronous and Asynchronous Requests Differ?
- •11.6 How Is a Server Started?
- •11.7 How Many Connections Can a Client Have?
- •11.8 What Happens When a Client Disconnects?
- •11.9 What Happens If a Client Dies?
- •11.10 What Happens If a Server Dies?
- •11.15 How Many Outstanding Requests Can a Client Make to a Server?
- •11.16 Can Server Functionality Be Extended?
- •11.17 Example Code
- •11.18 Summary
- •12.2 Client Boilerplate Code
- •12.3 Starting the Server and Connecting to It from the Client
- •12.4 Server Startup Code
- •12.5 Server Classes
- •12.6 Server Shutdown
- •12.7 Accessing the Server
- •12.8 Summary
- •13 Binary Types
- •13.1 Symbian OS EXEs
- •13.2 Symbian OS DLLs
- •13.3 Writable Static Data
- •13.4 Thread-Local Storage
- •13.5 The DLL Loader
- •13.6 UIDs
- •13.8 Summary
- •14 ECOM
- •14.1 ECOM Architecture
- •14.2 Features of an ECOM Interface
- •14.3 Factory Methods
- •14.4 Implementing an ECOM Interface
- •14.5 Resource Files
- •14.6 Example Client Code
- •14.7 Summary
- •15 Panics
- •15.2 Good Panic Style
- •15.3 Symbian OS Panic Categories
- •15.4 Panicking Another Thread
- •15.5 Faults, Leaves and Panics
- •15.6 Summary
- •16 Bug Detection Using Assertions
- •16.3 Summary
- •17 Debug Macros and Test Classes
- •17.1 Heap-Checking Macros
- •17.2 Object Invariance Macros
- •17.3 Console Tests Using RTest
- •17.4 Summary
- •18 Compatibility
- •18.1 Forward and Backward Compatibility
- •18.2 Source Compatibility
- •18.3 Binary Compatibility
- •18.4 Preventing Compatibility Breaks
- •18.5 What Can I Change Without Breaking Binary Compatibility?
- •18.6 Best Practice: Planning for Future Changes
- •18.7 Compatibility and the Symbian OS Class Types
- •18.8 Summary
- •19 Thin Templates
- •20.1 Class Layout
- •20.3 Parameters and Return Values
- •20.4 Member Data and Functional Abstraction
- •20.5 Choosing Class, Method and Parameter Names
- •20.7 Summary
- •21 Good Code Style
- •21.1 Reduce the Size of Program Code
- •21.2 Use Heap Memory Carefully
- •21.3 Use Stack Memory Carefully
- •21.5 Optimize Late
- •21.6 Summary
- •Glossary
- •Bibliography and Online Resources
- •Index
PANICKING ANOTHER THREAD |
251 |
CleanupStack::Pop(ptr2); // ...so the code never gets here
}
As a user, if making the chess move you’ve carefully worked out causes your game to panic and close, you’re probably not interested in the category or error code. Such precise details are irrelevant and the ”program closed” dialog is more irritating than helpful.
Don’t use panics except as a means to eliminate programming errors, for example by using them in assertion statements. Panicking cannot be seen as useful functionality for properly debugged software; a panic is more likely to annoy users than inform them.
15.4 Panicking Another Thread
I’ve shown how to call User::Panic() when you want the thread to panic itself. But how, and when, should you use RThread::Panic()? As I described earlier, the RThread::Panic() function can be used to kill another thread and indicate that the thread had a programming error. Typically this is used by a server to panic a client thread when the client passes a badly-formed request. You can think of it as server self-defense; rather than go ahead and attempt to read or write to a bad descriptor, the server handles the client’s programming error gracefully by panicking the client thread. It is left in a good state which it would not have been if it had just attempted to use the bad descriptor. Generally, in a case like this, the malformed client request has occurred because of a bug in the client code, but this strategy also protects against more malicious ”denial of service” attacks in which a client may deliberately pass a badly-formed or unrecognized request to a server to try to crash it. The Symbian OS client–server architecture is discussed in Chapters 11 and 12; the latter chapter includes sample code that illustrates how a server can panic its client.
If you have a handle to a thread which has panicked, you can determine the exit reason and category using ExitReason() and ExitCategory(). This can be useful if you want to write test code to check that the appropriate panics occur, say from assertions in your library code, to defend it against invalid parameters. Since you can’t ”catch” a panic, it’s not as straightforward as running the code in the main thread, passing in the parameters and checking that the code panics with the correct value. The checking code would never run, because the panic would terminate the main thread.
252 |
PANICS |
A solution is to run deliberately badly-behaved client code in a separate test thread, programmatically checking the resulting exit reasons and categories of the panicked thread against those you would expect to have occurred. You should disable just-in-time debugging for the duration of the test, so that only the test thread, rather than the emulator, is terminated. For example:
enum TChilliStrength
{
ESweetPepper,
EJalapeno, EScotchBonnet };
void EatChilli(TChilliStrength aStrength)
{
_LIT(KTooStrong, "Too Strong!"); __ASSERT_ALWAYS(EScotchBonnet!=aStrength,
User::Panic(KTooStrong, KErrAbort);
... // Omitted for clarity
}
// Thread function
TInt TestPanics(TAny* /*aData*/)
{// A panic occurs if code is called incorrectly EatChilli(EScotchBonnet);
return (KErrNone);
}
void TestDefence()
{
//Save current just-in-time status TBool jitEnabled = User::JustInTime();
//Disable just-in-time debugging for this test User::SetJustInTime(EFalse);
_LIT(KPanicThread, "PanicThread");
// Create a separate thread in which to run the panic testing RThread testThread;
TInt r = testThread.Create(KPanicThread, TestPanics,
KDefaultStackSize, NULL, NULL);
ASSERT(KErrNone==r);
// Request notification of testThread’s death (see Chapter 10) TRequestStatus tStatus;
testThread.Logon(tStatus);
testThread.Resume();
User::WaitForRequest(tStatus); // Wait until the thread dies
ASSERT(testThread.ExitType()==EExitPanic); // Test the panic reason is as expected ASSERT(testThread.ExitReason()==KErrAbort);
testThread.Close();
// Set just-in-time back to previous setting User::SetJustInTime(jitEnabled);
}
SUMMARY |
253 |
15.5 Faults, Leaves and Panics
A fault is raised if a critical error occurs such that the operating system cannot continue normal operation. On hardware, this results in a reboot. A fault can only occur in kernel-side code or a thread which is essential to the system, for example the file server, so typically you will not encounter them unless you are writing device drivers or uncover a bug in the OS. In effect, a fault is another name for a serious system panic.
Chapter 2 discusses leaves in more detail, but, in essence, they occur under exceptional conditions such as out of memory or the absence of a communications link. Unlike a panic, a leave should always be caught (”trapped”) by code somewhere in the call stack, because there should always be a top-level TRAP. However, if a leave is not caught, this implies that the top-level TRAP is absent (a programming error) and the thread will be panicked and terminate.
15.6 Summary
This chapter discussed the use of panics to terminate the flow of execution of a thread. Panics cannot be ”caught” like an exception and are severe, resulting in a poor user experience. For that reason, panics are only useful to track down programming errors and, on Symbian OS, are typically combined with an assertion statement, as discussed in the next chapter.
This chapter described the best way to identify panics and illustrated how to test the panics that you’ve added to your own code for defensive programming. It gave a few examples of commonly-encountered system panics and directed you to the Panics section of the system documentation for a detailed listing of Symbian OS system panics.