Pre-Conditions and Post-Conditions for Kernel APIs

Reference documentation for kernel side function APIs, specifies a wide variety of pre-conditions and post-conditions.

  • A pre-condition is something that must be true before using the function.

  • A post-condition is something that is true on return from the function.

Such conditions are expressed using a standard phrase, wherever possible, and this section explains what those conditions mean.

Where more than one pre-condition is stated for a given function, then you can assume that all pre-conditions apply. In this sense, there is an implied AND relation between conditions. For example, in the description of the function DObject::AppendFullName(), the following pre-conditions apply:

No fast mutex can be held.
Call in a thread context.

Both conditions must be true before calling the functions.

There are exceptions to this rule, where a precondition applies only if some other factor is true. For example, in the description of the function DObject::TraceAppendName(), you will find the following pre-conditions:

Call either in a thread or an IDFC context, if aLock=TRUE
Call in any context, if aLock=FALSE.

Clearly, only one pre-condition will apply, depending on the supplied value of the aLock argument.

A few conditions are self-explanatory, and these are not included in these lists.

NOTE that some familiarity with kernel side programming is assumed.

Pre-conditions

This describes the meaning of each precondition, and tries to provide insight as to why it needs to be satisfied, and explains what you need to do to ensure that the preconditions are met.

Calling thread must be in a critical section

Critical sections are sections of code that leave the kernel in a compromised state if they cannot complete. A thread enters a critical section by calling NKern::ThreadEnterCS() and leaves it by calling NKern::ThreadLeaveCS().

While in a critical section, the thread cannot be suspended or killed. These actions are deferred until the end of the critical section. If a thread takes an exception while inside a critical section, this is treated as fatal to the system, and cause a Kern::Fault().

The described function must be in a critical section when called.

Calling thread must not be in a critical section

Critical sections are sections of code that leave the kernel in a compromised state if they cannot complete. A thread enters a critical section by calling NKern::ThreadEnterCS() and leaves it by calling NKern::ThreadLeaveCS().

While in a critical section, the thread cannot be suspended or killed. These actions are deferred until the end of the critical section. If a thread takes an exception while inside a critical section, this is treated as fatal to the system, and cause a Kern::Fault().

There are some functions winthin the system that must NOT be in a critical section when called. This applies to the described functions.

Calling thread can either be in a critical section or not

Critical sections are sections of code that leave the kernel in a compromised state if they cannot complete. A thread enters a critical section by calling NKern::ThreadEnterCS() and leaves it by calling NKern::ThreadLeaveCS().

While in a critical section, the thread cannot be suspended or killed. These actions are deferred until the end of the critical section. If a thread takes an exception while inside a critical section, this is treated as fatal to the system, and cause a Kern::Fault().

When this pre-condition applies to the described function, it means that it does not matter whether the code is in a critical section or not.

No fast mutex can be held

A thread can hold no more than one fast mutex at any given time. The described function uses a fast mutex, which means that on entry to the function, the calling thread must not hold another one.

Kernel must be locked

Many kernel side functions run with interrupts enabled, including code that manipulates global structures, such as the thread ready list. To prevent such code from being reentered and potentially corrupting the structure concerned, a lock known as the kernel lock (sometimes referred to as the preemption lock) is used.

Sections of code that need to be protected against rescheduling call NKern::Lock(). Interrupt Service Routines (ISRs) can still run but no other thread (or IDFC) can run until the kernel is unlocked by a call to NKern::Unlock().

The pre-condition means that the kernel lock must be set before calling the described function.

Kernel must be unlocked

Many kernel side functions run with interrupts enabled, including code that manipulates global structures, such as the thread ready list. To prevent such code from being reentered and potentially corrupting the structure concerned, a lock known as the kernel lock (sometimes referred to as the preemption lock) is used.

Sections of code that need to be protected against rescheduling call NKern::Lock(). Interrupt Service Routines (ISRs) can still run but no other thread (or IDFC) can run until the kernel is unlocked by a call to NKern::Unlock().

The pre-condition means that the kernel lock must NOT be set when the described function is called.

Kernel can be locked or unlocked

Many kernel side functions run with interrupts enabled, including code that manipulates global structures, such as the thread ready list. To prevent such code from being reentered and potentially corrupting the structure concerned, a lock known as the kernel lock (sometimes referred to as the preemption lock) is used.

Sections of code that need to be protected against rescheduling call NKern::Lock(). Interrupt Service Routines (ISRs) can still run but no other thread (or IDFC) can run until the kernel is unlocked by a call to NKern::Unlock().

The pre-condition means that it does not matter whether the kernel lock is set or unset when the described function is called.

Kernel must be locked, with lock count 1

Many kernel side functions run with interrupts enabled, including code that manipulates global structures, such as the thread ready list. To prevent such code from being reentered and potentially corrupting the structure concerned, a lock known as the kernel lock (sometimes referred to as the preemption lock) is used.

Sections of code that need to be protected against rescheduling call NKern::Lock(). Interrupt Service Routines (ISRs) can still run but no other thread (or IDFC) can run until the kernel is unlocked by a call to NKern::Unlock().

In addition, calls to NKern::Lock() and NKern::Unlock() are counted. The count value is zero when the kernel is unlocked; a call to NKern::Lock() adds one to the count value, and a call to NKern::Unlock() subtracts one from the count value.

The pre-condition means that there must be exactly one call to Kern::Lock() that has not yet had a matching call to Kern::Unlock().

See also NFastMutex::Wait().

Interrupts must be enabled

This pre-condition states that interrupts must be enabled before calling the described function.

Possible reasons why interrupts may need to be enabled include:

  • the function needs interrupts to occur; for example, it may wait for timer ticks.

  • the function may take a long or potentially unbounded time to run, so interrupts need to be enabled to guarantee bounded interrupt latency.

See also the function NKern::RestoreInterrupts() and the associated function NKern::DisableInterrupts().

Interrupts must be disabled

This pre-condition states that interrupts must be disabled before calling the described function.

See also the function NKern::DisableInterrupts() and the associated function NKern::RestoreInterrupts().

Interrupts can either be enabled or disabled

This pre-condition states that it does not matter whether interrupts are enabled or disabled before calling the described function.

System lock must be held

The system lock is a specific fast mutex that only provides exclusion against other threads acquiring the same fast mutex. Setting, and acquiring the system lock means that a thread enters an implied critical section.

The major items protected by the system lock are:

  • DThread member data related to thread priority and status.

  • the consistency of the memory map; on the kernel side, the state of user side memory or the mapping of a process is not guaranteed unless (a) you are a thread belonging to the process that owns the memory or (b) you hold the system lock.

  • the lifetime of DObject type objects and references to them, including handle translation in Exec dispatch.

Note that the system lock is different from the kernel lock; the kernel lock protects against any rescheduling. When the system lock is set, the calling thread can still be pre-empted, even in the locked section.

The system lock is set by a call to NKern::LockSystem().

The pre-condition means that the system lock must be set before calling the described function.

System lock must not be held

See the pre-condition System lock must be held for a brief description of the system lock.

The system lock is unset by a call to NKern::UnlockSystem().

The pre-condition means that the system lock must not be set before calling the described function.

Call in a thread context

This pre-condition means that the described function must be called directly, or indirectly, from a DFC or a thread. The thread can be a user thread or a kernel thread.

Call in an IDFC contex

This pre-condition means that the described function must be called directly, or indirectly, from an IDFC.

Note that when calling a function from an IDFC:

  • the kernel is locked, so pre-emption is disabled

  • user memory cannot be accessed

  • the function cannot block or wait.

Call either in a thread or an IDFC context

This pre-condition means that the described function can be called directly, or indirectly, from an IDFC, a DFC or a thread. The thread can be a user thread or a kernel thread.

Note that when calling a function from an IDFC:

  • the kernel is locked, so pre-emption is disabled

  • user memory cannot be accessed

  • the function cannot block or wait.

Call in any context

This pre-condition means that the described function can be called directly, or indirectly, from an IDFC, a DFC or a thread, or it can be called from an Interrupt Service Routine (ISR).

A thread can be a user thread or a kernel thread.

Note that when calling a function from an IDFC:

  • the kernel is locked, so pre-emption is disabled

  • user memory cannot be accessed

  • the function cannot block or wait.

Do not call from an ISR

This pre-condition means that the described function must not be called from an Interrupt Service Routine (ISR).

Note that ISRs have the following characteristics:

  • they have an unknown context

  • they must not allocate or free memory

  • they cannot access user memory

  • they must not call functions that interfere with critical sections of code.

The calling thread must own the semaphore

A semaphore can be waited on only by the thread that owns it. This precondition is needed when the described function calls a semaphore wait function.

The calling thread must not be explicitly suspended

This refers to nanokernel threads, not Symbian platform threads. The described function must not be used if the thread is in the suspended state. One of the possible reasons for this is that the described function does not check the thread's suspend count.

A thread may be created suspended, or the thread may be put into a suspended state using NThreadBase::Suspend(). If you don't know whether or not the thread is suspended, use NThreadBase::CheckSuspendThenReady().

See also NThreadBase::Resume() and NThreadBase::ForceResume().

Note that these functions are for use only in an RTOS personality layer.

The calling thread must hold the mutex

The calling thread has waited for a mutex and acquired it. This precondition is needed when the thread is about to release the mutex, ie call one of the Signal() functions.

Call only from ISR, IDFC or thread with the kernel locked

See the pre-condition Kernel must be locked.

Call only from IDFC or thread with the kernel locked

See the pre-condition Kernel must be locked.

Do not call from thread with the kernel unlocked

See the pre-condition Kernel must be unlocked.

Do not call from ISR or thread with the kernel unlocked

See the pre-condition Kernel must be unlocked.

Thread must not already be exiting

The pre-condition means that the described function should not be called after the thread has been killed.

In EKA2, threads do not die immediately, they are placed on a queue to be deleted later.

Functions with this pre-condition are not likely to used in a device driver.

Property has not been opened

A property is a single value used by “Publish and Subscribe”. Each property must be opened before it can be used. To open a property, use either RPropertyRef::Attach() or RPropertyRef::Open(). Once opened, the property cannot be opened again.

The pre-condition means that the property must NOT already be open when the described function is called.

Property has been opened

A property is a single value used by “Publish and Subscribe”. Each property must be opened before it can be used. To open a property, use either RPropertyRef::Attach() or RPropertyRef::Open(). Once opened, the property cannot be opened again.

The pre-condition means that the property must already be open before calling the described function.

Must be called under an XTRAP harness or calling thread must not be in a critical section

Each Symbian platform thread can be associated with a kernel-side exception handler, set using XTRAP(); for example, to detect bad memory accesses.

The described function can legitimately cause an exception, and the pre-condition means that

either:

  • the described function should be called inside an XTRAP() harness to catch the exception

or

  • the thread must not be in a critical section, because exceptions are not allowed inside them. If a thread takes an exception while inside a critical section, this is treated as fatal to the system, and causes a Kern::Fault().

DObject::Lock fast mutex held

The described function accesses an object whose internal data needs to be protected by the specified fast mutex.

The operations of:

  • obtaining an object’s name

  • setting an object's name

  • setting an object's owner

are all protected by the global fast mutex, DObject::Lock. This is done to avoid inconsistent results caused by, for example, one thread renaming an object while another is reading its name or full name.

Setting the owner is protected as the owner's name is part of the object's full name.

DCodeSeg::CodeSegLock mutex held

The DCodeSeg::CodeSegLock mutex protects the global code graph from simultaneous accesses. Wait on this mutex before calling the described function, e.g. by calling DCodeSeg::Wait().

Any kind of lock can be held

The described function can be called with any kind of lock.

Call only from Init1() in base port

The pre-condition means that the described function can only be called during the first phase of kernel initialisation, i.e. during execution of the Base Port implementation of Asic::Init1(). See the Board Support Packages Quick Start.

This condition may apply because the described function:

  • must be called before any context switch

  • must be called before the MMU is turned on.

The various parameters must be valid. The PIL or PSL will fault the kernel if not

This pre-condition refers to a DMA request.

The parameters used when calling the described function must be as specified. If they are not, the platform independent layer or the platform specific layer cannot recover and will cause the kernel to fault, i.e. the device will reset.

The request is not being transferred or pending

This pre-condition refers to a DMA request.

The described function must not be called if a DMA request has already been set up, or is in progress. A possible reason for this is that the described function resets parameters that have been already setup.

Wait on TimerMutex before calling this

The TimerMutex mutex protects the timer queues. Wait and signal operations on the timer mutex are accomplished with the static functions TTickQ::Wait() and TTickQ::Signal(). This precondition is necessary when the described function manipulates the timer queue.

Message must be in ACCEPTED state

This pre-condition indicates that the message has been read by the receiving thread. It is not attached to a message queue but is currently in use by the receiving thread.

Queue must not be in asynchronous receive mode

This pre-condition refers to kernel side message queues. A kernel thread can receive messages:

A possible reason for this precondition is that the queue is about to be polled.

The queue may be polled either:

  • before the first Receive() is issued

  • while processing a message but before Receive() is called again

Container mutex must be held / Thread container mutex must be held / Process container mutex must be held

Each of the containers is protected by a mutex.

The pre-condition means that the calling thread must acquire the relevant mutex before calling the described function. Object containers are DObjectCon types, and the relevant mutexes are acquired and freed by calling Wait() and Signal() on the container object.

Post-conditions

A post condition describes what is true on return from a kernel API function.

Calling thread is in a critical section

The code is in a critical section on return from the function.

See also the pre-condition: Calling thread must be in a critical section.

Calling thread is not in a critical section

The code is NOT in a critical section on return from the function.

See also the pre-condition: Calling thread must not be in a critical section.

No fast mutex is held

A thread can hold no more than one fast mutex at any given time. A fast mutex is NOT held on exit from the function.

Kernel is locked

The post-condition means that, on exit from the described function, the kernel lock is on. The described function might have explicitly set the kernel lock or, more commonly, the lock was set before entry to the described function, and has not been unset by that function.

See also the pre-condition Kernel must be locked.

Kernel is unlocked

The kernel is NOT locked on exit from the described function. The described function might have explicitly unset the kernel lock or, more commonly, the lock was not set before entry to the described function, and has not been set by that function.

See also the pre-condition Kernel must be unlocked.

Kernel is locked, with lock count 1

See the pre-condition Kernel must be locked, with lock count 1.

Interrupts are enabled

This post-condition states that interrupts are enabled on return from the described function.

See the pre-condition Interrupts must be enabled

Interrupts are disabled

This post-condition states that interrupts are disabled on return from the described function.

See the pre-condition Interrupts must be disabled

System lock is held

This post-condition states that the system lock is held on return from the described function.

The system lock is a specific fast mutex that only provides exclusion against other threads acquiring the same fast mutex. Setting, and acquiring the system lock means that a thread enters an implied critical section.

The major items protected by the system lock are:

  • DThread member data related to thread priority and status.

  • the consistency of the memory map; on the kernel side, the state of user side memory or the mapping of a process is not guaranteed unless (a) you are a thread belonging to the process that owns the memory or (b) you hold the system lock.

  • the lifetime of DObject type objects and references to them, including handle translation in Exec dispatch.

Note: System lock is different from the kernel lock; the kernel lock protects against any rescheduling. When the system lock is set, the calling thread can still be pre-empted, even in the locked section.

The system lock is set by a call to NKern::LockSystem(), and unset by call to NKern::UnlockSystem().

The calling thread holds the mutex

The calling thread has waited for a mutex and acquired it. On return from the described function, the thread still holds the mutex.

See also the pre-condition The calling thread must hold the mutex.

Container mutex is held / Thread container mutex is held / Process container mutex is held

Each of the containers is protected by a mutex.

The post-condition means that the calling thread has the relevant mutex on return from the described function. This is most likely because the mutex was acquired before entering the described function.

Object containers are DObjectCon types, and the relevant mutexes are acquired and freed by calling Wait() and Signal() on the container object.

DCodeSeg::CodeSegLock mutex held

The DCodeSeg::CodeSegLock mutex protects the global code graph from simultaneous accesses.

This post condition means that the mutex is held on exit. The most common situation is that the mutex was acquired before entry to the described function. Relinquish the mutex by calling DCodeSeg::Signal().