Heap Locking in the Font and Bitmap Server

The Font and Bitmap Server's heap locking API is deprecated in Symbian OS v9.3 and later versions. This topic explains the rationale for this and how it affects existing applications that use the CFBsBitmap::LockHeap(), CFBsBitmap::LockHeapLC() and CFbsBitmap::UnlockHeap() functions.

Heap locking in previous versions

Prior to Symbian OS v9.3, the Font and Bitmap Server made a distinction between small bitmaps and large bitmaps. The data for large bitmaps was stored in a separate global memory heap where a de-fragmentation algorithm was used. When necessary, this algorithm moved bitmap data in the virtual address space. As a result it was possible for an application to allocate a new bitmap and trigger the de-fragmentation algorithm while an unrelated application accessed the data of another bitmap. From an application's point of view, this meant that the value returned by CFbsBitmap::DataAddress() could change unpredictably at any time.

The solution was to lock the global memory heap by calling CFbsBitmap::LockHeap() or CFbsBitmap::LockHeapLC() before calling CFbsBitmap::DataAddress(). When the application finished accessing the bitmap data, it unlocked the global memory heap by calling CFbsBitmap::UnlockHeap(). Internally the Font and Bitmap Server used a global mutual exclusion object to implement the heap locking functions and waited on it before performing any operation that could trigger the de-fragmentation algorithm.

Although it was undocumented functionality, multi-threaded applications could use the heap locking functions as a way of synchronizing access to bitmaps shared among several threads, because calls to CFbsBitmap::LockHeap() and CFbsBitmap::UnlockHeap() translated into Wait() and Signal() operations on the global mutual exclusion object.

Removal of heap locking

Use of a disconnected memory chunk

From Symbian OS v9.3, all bitmap data is kept in a disconnected global memory chunk. This is a new type of chunk that does not require the set of pages committed to physical memory to form a contiguous block in the virtual address space. A virtual address range much bigger than the amount of physical memory is reserved, and pages are committed to physical memory when allocating bitmaps and de-committed when freeing them.

As a result, fragmentation in the reserved virtual address range is not a problem and fragmentation in the physical address space is handled transparently by the Memory Management Unit (MMU). Therefore, de-fragmentation is not necessary and bitmap operations do not need to move data belonging to other bitmaps.

Removal of in-place operations

Some bitmap operations, such as CFbsBitmap::Resize(), CFbsBitmap::Compress() and CFbsBitmap::CompressInBackground(), can change the value returned by CFbsBitmap::DataAddress() because they may need to re-allocate memory. These functions have a new implementation. They now create a new bitmap object inside the Font and Bitmap Server and update the reference contained in the CFbsBitmap object. The old bitmap is destroyed immediately if it is referenced only by the CFbsBitmap object on which the re-allocating function was called. Otherwise, the old bitmap is flagged as dirty and its destruction is deferred until its reference count becomes zero.

All of the functions in the CFbsBitmap class now check whether the referenced bitmap is dirty before proceeding. If it is, the Font and Bitmap Server updates the reference to point to the new bitmap. When all of the CFbsBitmap objects that referenced the old bitmap have had their references updated or have been deleted, the reference count of the old bitmap becomes zero and it is destroyed.

The impact on performance of this change is negligible, because the dirty flag is tested in the context of the client thread and in most cases bitmaps are "clean".

Sometimes multiple CFbsBitmap objects in different client threads reference the same bitmap. When a client thread calls a re-allocating function on a bitmap while another client thread is accessing the bitmap data through a pointer returned by CFbsBitmap::DataAddress(), there is no illegal memory access because the old bitmap still exists. This scenario typically occurs when an application calls CFbsBitmap::CompressInBackground() on a bitmap and continues to use it, because the compression is performed asynchronously at an unspecified time.

Deprecation of the heap locking functions

The use of a disconnected memory chunk and the removal of in-place operations allow the Font and Bitmap Server to work without heap locking. Therefore, CFbsBitmap::LockHeap(), CFbsBitmap::UnlockHeap() and CFbsBitmap::LockHeapLC() are no longer necessary and have been deprecated. These functions no longer provide any locking functionality and cannot be used as a synchronization mechanism.

It is recommended that you replace all calls to CFbsBitmap::LockHeap() and CFbsBitmap::UnlockHeap() with calls to the new functions CFbsBitmap::BeginDataAccess() and CFbsBitmap::EndDataAccess(). If necessary also add a mutual exclusion object to multi-threaded applications. Although not strictly necessary, calling CFbsBitmap::BeginDataAccess() and CFbsBitmap::EndDataAccess() ensures optimum performance in platforms with graphics hardware acceleration.

For the benefit of legacy applications that do not require changes, CFbsBitmap::LockHeap() and CFbsBitmap::UnlockHeap() now simply call CFbsBitmap::BeginDataAccess() and CFbsBitmap::EndDataAccess(), respectively.

Any number of client threads can now call CFbsBitmap::DataAddress() and manipulate bitmap data at the same time. This does not cause a problem provided each client thread accesses a different bitmap. If a multi-threaded application shares a bitmap among several threads and assumes that CFbsBitmap::LockHeap() is implemented as a Wait() operation on a mutual exclusion object, you may need to modify the application.

The impact of the change on existing applications

The old documentation was ambiguous about several aspects of the semantics of the heap locking API. However, because the actual implementation used a global mutual exclusion object, it was possible for a client thread to call CFbsBitmap::LockHeap() and prevent any other client thread that called CFbsBitmap::LockHeap() from proceeding. This included client threads that attempted to access the same bitmap and totally unrelated client threads. Therefore, the heap locking API may have been used as a synchronisation mechanism in multi-threaded applications. This is no longer possible.

The SYMBIAN_DEBUG_FBS_LOCKHEAP macro can be used to help detect multi-threaded applications that share a bitmap among several threads and fail to provide their own mutual exclusion mechanism. When this macro is defined in debug builds of the Font and Bitmap Server, CFbsBitmap::LockHeap() and CFbsBitmap::UnlockHeap() check whether more than one client thread has a call to CFbsBitmap::LockHeap() on the same bitmap at the same time without a corresponding call to CFbsBitmap::UnlockHeap(). When this is detected, a panic FBSCLI 22 is raised.

The impact of the change on existing multi-threaded applications that use the heap locking API as a synchronisation mechanism is reduced, in most of the cases, to the possibility of loss of bitmap data changes rather than illegal memory access or any other kind of abnormal termination. An exception is the case of existing multi-threaded applications that use the heap locking API as a synchronisation mechanism where one thread calls CFbsBitmap::Resize() on a bitmap while another thread calls CFbsBitmap::SizeInPixels() followed by CFbsBitmap::DataAddress() on the same bitmap. This can produce an incorrect value for the size in pixels and lead to an illegal memory access.