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.
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.
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.