- •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
EXCEPTION HANDLING |
163 |
RThread::ReadL() can be used to transfer data into the currently running thread from the thread represented by a valid RThread handle. ReadL() transfers data from a descriptor in the ”other” thread, identified as a const TAny pointer, into a modifiable descriptor (8- or 16-bit) in the current thread. The function leaves with KErrBadDescriptor if the TAny pointer does not point to what appears to be a valid descriptor. The length of the target descriptor can be determined before reading from it by a call to RThread::GetDesLength().
Inter-thread data transfer uses descriptors because they fully describe their length and maximum allowable length, which means that no extra parameters need to be transferred to indicate this information. If you want to transfer non-descriptor data, you can use the package classes TPckgC and TPckg, described in Chapter 6, which effectively ”descriptorize” the data. Package classes are only valid for transferring a flat data structure such as that found in a T class object; their use in inter-thread data transfer between client and server threads is discussed in Chapter 12.
10.5 Exception Handling
On EKA1, the RThread API supports thread exception management. On the more secure EKA2 platform, this has moved into class User and applies only to the current thread. The signatures of the five functions are the same, however, regardless of the class that implements them.
TExceptionHandler* ExceptionHandler() const;
TInt SetExceptionHandler(TExceptionHandler* aHandler, TUint32 aMask); void ModifyExceptionMask(TUint32 aClearMask, TUint32 aSetMask);
TInt RaiseException(TExcType aType);
TBool IsExceptionHandled(TExcType aType);
SetExceptionHandler() allows you to define an exception handler function for the thread for which the handle is valid. It takes an exception-handling function and a bitmask that allows you to specify the category of exception which will be handled (see e32std.h).
const TUint KExceptionAbort=0x01; const TUint KExceptionKill=0x02;
const TUint KExceptionUserInterrupt=0x04; const TUint KExceptionFpe=0x08;
const TUint KExceptionFault=0x10; const TUint KExceptionInteger=0x20; const TUint KExceptionDebug=0x40;
If an exception is raised on the thread that falls into the category handled, it will be passed to the specified exception handler to be dealt with.
164 |
SYMBIAN OS THREADS AND PROCESSES |
The exception handler function, TExceptionHandler, is a typedef for a function which takes a value of TExcType (an enumeration which further identifies the type of exception) and returns void. Each of the TExcType enumeration values each correspond to one of the categories above but they identify the exception further, for example EExcBoundsCheck, EExcInvalidOpCode and EExcStackFault are all types of the KExceptionFault category.
What should an exception handler function do? Well, you cannot identify where the problem occurred from within the handler, so the only thing you can do is leave from the handler function. Effectively, the handler converts an exception into a leave which can be caught and dealt with. However, by doing this, you require a TRAP around every operation that could generate an exception of that type. This could have a significant impact in terms of code size and speed (as discussed in Chapter 2) and as a result, while this technique might be useful for debugging, it is not recommended for code you intend to release.
Without a handler specified, the exception terminates the thread with a KERN-EXEC 3 panic if it occurs during a kernel executive call, or a USER-EXEC 3 panic if it occurs in a non-system call.
10.6Processes
I introduced processes in Chapter 8 and briefly mentioned them in this chapter when discussing relative thread priorities. The RProcess class can be used to get a handle to a running process, in much the same way as RThread for a running thread. RProcess is defined in e32std.h and a number of the functions look quite similar to those in
RThread.6
You can use the default constructor to create a handle to the current process, the Create() function to start a new, named process and the Open() function to open a handle to a process identified by name or process identity (TProcessId). The Resume() functions and assorted functions to stop the process will also look familiar. Note that there is no Suspend() function because processes are not scheduled; threads form the basic unit of execution and run inside the protected address space of a process.
Chapter 13 discusses the difference between the emulator and target hardware in more detail, but one aspect is particularly relevant here. On Windows, the emulator runs within a single Win32 process, EPOC.exe, and each Symbian OS process runs as a separate thread inside it.
6 The SDK documentation for RThread and RProcess is extensive and should be consulted for further details of both these classes. Like the RThread class, RProcess has been secured in EKA2, resulting in the removal of some functions and the restriction of others to apply only to the currently running process
PROCESSES |
165 |
On EKA1 the emulation of processes on Windows is incomplete and
RProcess::Create() returns KErrNotFound. To emulate any code that spawns a separate process, e.g. server startup code, different source code is required for emulator builds, using threads instead of processes in which to launch the server. The EKA2 release has removed this inconvenience. While Symbian OS still runs in a single process on Windows (indeed, that’s what the ”S” in WINS, WINSCW and WINSB stands for), the emulation is enhanced and you can use the same code, calling RProcess::Create() on both platforms. Symbian OS, for emulator builds, translates this call to creation of a new Win32 thread within EPOC.exe.
I’ll conclude this chapter with a brief description of the most fundamental process within Symbian OS, the kernel server process, and the threads which run within it. I’ll restrict the discussion to Symbian OS EKA1 – you will be able to find more detail of the changes introduced by EKA2 from the relevant system documentation (www.symbian.com/technology/ product descriptions.html). On EKA1, the kernel server process is a special process that contains two threads which run at supervisor privilege level:
•the kernel server thread
This is the first thread to run in the system and has the highest priority of all threads. It implements all kernel functions requiring allocation or deallocation on the kernel heap and manages hardware resources. Device drivers run in the kernel thread and use interrupt service routines and delayed function calls to service events from the hardware.
•the ”Null” or ”Idle” thread
This has the lowest priority in the system and therefore runs when no other threads are ready to run. It switches the processor into idle mode to save power, by calling a callback function to shut down part of the hardware. This allows Symbian OS to conserve power most efficiently, thus maximizing the battery life. To allow the Null thread to run, your code should wait on events (typically using active objects or kernel signaling primitives such as mutexes) rather than polling constantly. A polling thread prevents the Null thread from running and stops Symbian OS from entering its idle, power-saving, mode.
The other threads in the system are known as user threads and run in an unprivileged state. User threads can access kernel services, such as other threads or processes, using the APIs in the user library (EUser.dll). When a user thread requires a kernel service, EUser either switches to the kernel executive, which runs kernel-privileged code in the context of the running thread, or makes a call via the executive into the kernel server process.
166 |
SYMBIAN OS THREADS AND PROCESSES |
10.7Summary
This chapter compared the use of pre-emptively scheduled threads on Symbian OS with cooperatively scheduled active objects. Active objects are preferred for most multitasking code because they have lower runtime overheads. However, threads are sometimes necessary, for example if priority-based pre-emption is required or to perform a long-running background task asynchronously where it cannot be split into increments.
The chapter reviewed the RThread class, which represents a handle to a thread on Symbian OS. It identified the differences in the RThread API between EKA1 and EKA2 releases, because EKA2 has removed the facility for one thread to affect any other thread running in a separate process. The RThread API allows a thread handle to rename a thread, modify its priority and suspend, terminate or panic it. It also provides a means by which data may be transferred between threads (inter-thread data transfer is discussed in more detail in Chapters 11 and 12).
The chapter concluded by discussing Symbian OS processes in brief, and included a short description of the threads running with the EKA1 kernel server process.
The use of active objects for multitasking on Symbian OS is discussed in Chapters 8 and 9.
11
The Client–Server Framework
in Theory
In theory there is no difference between theory and practice. In practice there is
Yogi Berra
This chapter examines the client–server framework model on Symbian OS. I’ll be discussing the theory, such as why client–server is used on Symbian OS, how it works and the implementation classes involved. There’s a lot of information, so I’ll use a question and answer structure to break the chapter down clearly into logical sections.
When programming for Symbian OS, it is likely that you will often use system servers transparently, via classes that wrap the actual details of client–server communication. It’s less common to implement your own server or write client-side code that accesses a server directly. So in this chapter, I’ll make it clear which information you can skip if you really only want to know the basics of the Symbian OS client–server architecture and how to use a server as a typical client. I’ll illustrate the chapter with examples of how to use a typical Symbian OS system server, the file server.
The next chapter works through some example code for a Symbian OS client and server, to illustrate how a typical implementation is put together and explain how to go about writing your own.
This chapter discusses the client–server model for Symbian OS releases up to and including v7.0s (the code samples use the client–server APIs from Symbian OS v7.0). On Symbian OS v8.0, the concepts are generally the same, but some of the APIs have changed. Rather than confuse matters, I’ve decided to concentrate solely on the releases available at the time of going to press. You can find more information about the differences in v8.0 from the relevant v8.0 SDKs when they become available, from other Symbian press books and, I hope, later editions of this book, or from the Symbian website (www.symbian.com/technology.html).