Two of the primary differences between Palm OS Cobalt and earlier versions of Palm OS are the introduction of protected memory and multi-threading. This chapter discusses these features and how you can make use of them in your Palm OS applications.
This chapter is divided into the following broad topics:
Architecture Overview
Using the Threading APIs
Summary of Threading
Architecture Overview
To understand the Palm OS Cobalt architecture, you must understand some of the terminology used in Palm OS Cobalt and how those terms compare with their use in other operating systems. Note that not all of the features described here are available in the SDK.
Threads and Scheduling
A thread is autonomous unit of execution with its own set of registers, stack, program counter, and other state needed to execute code. Palm OS Cobalt allows for multiple threads executing simultaneously. A thread that would like to execute code is in a READY state. When two or more threads are ready to run, the system schedules them based on their priority, using round-robin scheduling for multiple threads with the same priority.
Table 8.1 lists the possible states for a Palm OS Cobalt thread:
The thread is running. That is, it is currently executing code. |
|
The thread is ready to run, but because it is not the highest priority thread it is queued on the ready list. |
|
The thread is blocked, waiting for some condition to clear or resource to become available. A thread can wait on one or more other threads by calling |
|
The thread is suspended. A suspended thread does not resume execution until it has been resumed. You suspend a thread by calling |
|
Thread priority levels range from 1 (best) to 255 (worst). The thread with the best priority that is ready to run is always scheduled at the next context switch, and all threads of worse priority are stopped until the best-priority thread has blocked. Although thread priorities range from 1 to 255, in reality user threads are typically restricted to a priority no better than 30 (sysThreadPriorityBestUser
). See "Thread Priorities" for a list of commonly-used thread priorities.
Palm OS Cobalt allows for thread-specific data. This takes the form of slots containing integer values, with each thread having its own value in a particular slot.
Processes and Applications
A process is a protected environment in which one or more threads reside. A thread must reside in one and only one process. The process provides that thread's heap and other global state, executable code, and communication channels with other parts of the system. The system enforces restrictions on access to memory and other resources between processes. It is not, by default, possible for a thread in one process to access the resources in another process, although such access may be granted if requested. Palm OS Cobalt provides for shared memory areas that are accessible to multiple processes. All threads running in a particular process have unrestricted access to all resources in that process. Figure 8.1, below, illustrates the Palm OS Cobalt processes and the access they have to the various local and shared memory areas.
Figure 8.1 Process-to-Memory Mapping

Note that third-party developers can write code that runs in either the Application, Background, or Sublaunch processes (and note that there are up to three Sublaunch processes in Palm OS Cobalt).
Dynamic creation of processes is not supported in Palm OS Cobalt. The system creates a fixed set of processes while booting up and may create and destroy some well-defined processes on behalf of applications as needed. Due to restrictions on the total number of available processes, you cannot create new processes in an ad-hoc manner.
Applications and Sublaunching
An application runs in its own process, called the Application process. Only one application runs at a time. When an application switch occurs, the currently-running application exits, and its process is torn down. A new Applicationprocess is then created, and the new application is started within it.
When an application asks another application to perform some service on its behalf, this is known in Palm OS as a sublaunch. When a sublaunch occurs, the sublaunched code is loaded into the Application process and executed in-place. This is intrinsically unsafe, however, becuase the sublaunched code has complete access to the hosting application. Because of this, Palm OS Cobalt provides for remote sublaunching. During a remote sublaunch, a new temporary process is created in which the target code is executed. This process is granted temporary status as the Application process until the sublaunch completes. It can do anything that a normal application can. While the remote sublaunched code is executing, the main application is completely blocked, so there is effectively still only one flow of execution.
All occurrences in the system of sublaunches that are outside of the application's direct control (such as notifications) are performed as a remote sublaunch.
Background Threads
Palm OS Cobalt also creates a process called the Background Process. Applications can use this process to execute code that needs to persist across application switches. (Any threads created in the Application process are torn down with the rest of the process as part of an application switch.) The system provides APIs for applications to spawn threads in the Background Process, which are then free to run independently from the main UI.
Note that code from multiple independent applications may be running in the Background Process, and any application is free to load code into the process as desired. Because of this, the Background Process is not a secure address space. Secure operations must be executed in the Application process, where the application has full control over what is loaded. In addition, crashing code will bring down all other threads running in the Background Process. There are facilities for applications to be notified of a thread crashing so they may restart any desired threads.
Thread Synchronization
Palm OS Cobalt provides two major categories of synchronization primitives. Traditional primitives are implemented in the kernel and can be used to synchronize threads across processes. Lightweight primitives are implemented in user space and can only be used to synchronize threads in the same process. The main goal of the lightweight primitives is to reduce resource usage, as they require no kernel state and as little as four bytes of storage. However, they also tend to have better performance—any operation that does not require blocking or interacting with another blocking thread can be done entirely in user space without a kernel call.
Traditional Synchronization Primitives
Among the traditional synchronization primitives that Palm OS Cobalt supports are mutexes and counted semaphores.
A mutex is a simple locking primitive. Only one thread can hold a mutex at a time; all other threads trying to acquire the mutex are blocked until the first thread releases it, at which point the next thread gains access to the mutex and continues its execution.
A counted semaphore provides a very general synchronization primitive, which can be used to construct many other types of synchronization semantics (mutex, reader/writer, producer/consumer, and so on). A variation on this, the fast semaphore, is a more efficient implementation for threads in the same process—but most code should use a lightweight critical section instead.
Lightweight Synchronization Primitives
In addition to mutexes and counted semaphores, Palm OS Cobalt also supports critical sections and condition variables, both of which fall under the classification of "lightweight primitives."
A critical section is the lightweight version of a mutex. A critical section is simply a set of statements that can only be executed by a single process at any given time.
A Palm OS Cobalt condition variable provides a subset of traditional condition variable semantics. The operations supported in Palm OS Cobalt are:
- Close
- Open
- Wait: causes the thread to block until the condition variable is open
- Broadcast: causes all waiting threads to continue
Condition variables can be combined with critical sections similar to POSIX semantics—the critical section is atomically released and acquired while blocked on the condition variable.
Inter-Process Communication
There are a variety of methods in Palm OS Cobalt for getting data from one process to another. The following sections briefly summarize them, from the lowest level up.
Shared Memory
There are a variety of services in the system that can be placed under the category of shared memory. While many of these don't actually use shared memory for their implementation, relying instead on IPC or other communication mechanisms, they all share the key characteristic of placing data in a common location that threads can access in an ad-hoc manner. These provide little to no support for synchronization and no way to target data to specific threads.
Feature Manager
The Feature Manager is a simple name-space mapping of tuples (creator ID, feature number) to integer values. In Palm OS Cobalt the Feature Manager is a system service, so the data it contains is global to the system. A thread in one process can place a value in the Feature Manager, which can then be read from any other process. In Palm OS Cobalt there is no concept of an owner for a feature, so the system cannot remove features created by an application after it has gone away. Accordingly, all data in the Feature Manager is lost upon a system reset.
Data Manager
The Data Manager maintains a system-global pool of persistent data. While there is some access control available in the Data Manager APIs, this can be generally considered a shared memory area. Because data here is persistent, it generally isn't useful for passing transient data across processes. Like the Feature Manager, there is no strong concept of ownership, so the system cannot easily clean up data that is no longer needed.
Memory Dealer
While the Palm OS Cobalt kernel fully supports memory segments and access control to them across processes, limitations on the total number of available segments means that applications can't directly make use of this facility. This is the underlying reason for the limitation on number of processes.
Events
Traditional Palm OS events can be delivered to the currently running application from any other process. There are also APIs for sending events to specific windows and event queues—but note that these only work within the same process.
Named Pipes
Threads can use IOS to create a named pipe, through which other threads can rendezvous for communication. These can be created and accessed by any thread in any process. The traditional IOS APIs are used to send and receive data, and both data and IOS file descriptors can be sent through a named pipe.
Graphics Context
All of the traditional UI APIs in Palm OS Cobalt are associated with a "graphics context," which is bound by thread. There are APIs available for creating and destroying the graphics context associated with a thread; these APIs can be used from any process. For example, a thread running in the Background Process can create a graphics context, bring up a dialog, and then close the dialog, destroy the context, and continue. The application's thread—known as the Main UI Thread—is given a special graphics context with additional features not available to other threads; these additional features are needed for backward compatibility. Windows in the Main UI Thread can be back-buffered, and the thread also serves as the target for all application-related events.
There is no way to directly communicate between different threads doing UI—even in the same process, the threads have completely separate graphics contexts. As mentioned above, there are some APIs that allow events to be delivered to the event queue of a graphics context for communication with its thread.
Using the Threading APIs
The APIs for the features previously described come from a variety of places in the system. Many are slight abstractions on top of the low-level kernel API in the System Manager. Others are part of the UI and application model; these are declared in Event.h
and Window.h
.
The main APIs in the SDK are essentially the traditional Palm OS APIs working on an underlying protected memory system.
Application Launching
Most of the traditional Palm OS application APIs exist in Palm OS Cobalt and work as in previous versions of the OS. In particular, SysAppLaunch()
performs a sublaunch in the same address space as the current application, and SysUIAppSwitch()
performs a switch to a new application. Unlike previous versions of the OS, an application switch results in the current application's process being torn down and restarted, so no local state, such as memory chunks, can persist across switches.
SysAppLaunchRemote()
is like SysAppLaunch()
, but performs the sublaunch in a separate, freshly-created process. This allows applications to perform sublaunches of code that isn't trusted without compromising their own security.
SysNotifyBroadcastDeferred()
allows notifications to be sent from outside of the Main UI Thread. Like SysAppLaunchDeferred()
, this function is asynchronous, so no result is returned directly from the function.
Launching in the Background Process
Use EvtCreateBackgroundThread()
to create a new thread in the Background Process and launch the specified PRC in that thread.
These threads in the Background Process run independently of the main application—they can continue running through any application switches. In the case of the Background Process crashing and restarting, applications can register to receive a notification of this event in order to restart any needed background threads.
There is a limited set of things these background threads can do. Most Palm OS APIs work from within the Background Process—including IOS, Data Manager, and Feature Manager functions—but there are a number that are not allowed. Some of the more important ones include:
-
SysAppLaunch()
andSysAppLaunchRemote()
can only be called from the main UI thread. -
SysNotifyBroadcast()
can only be called from the main UI thread. UseSysNotifyBroadcastDeferred()
from other threads.
Manipulating Threads
The APIs in SysThread.h
provide a simple mechanism for creating threads. They allow developers to implement multi-threading in their own application, and not just in the special environment of the Background Process. This is especially useful for I/O, so that network and other I/O operations don't stop the application's UI or force you into a convoluted programming model.
Palm OS Cobalt provides a number of functions for creating and managing threads. These are defined in SysThread.h
.
SysThreadCreate()
and SysThreadStart()
are used together to create and then start a thread. They allow control over the new thread's priority, stack size, and entry function. The resulting thread can exit either by returning from its initial function or with an explicit call to SysThreadExit()
. Use SysThreadDelay()
to cause a thread to sleep for a specified amount of time.
NOTE: The application's primary thread is always created with the default stack size (the default stack size is set by the licensee for each device, and thus is not a constant value across the range of Palm Powered devices). Applications should avoid doing operations that need excessive stack in the primary thread. Instead, spawn new threads to perform such operations.
SysThreadInstallExitCallback()
allows the current thread to install a function that is executed when the thread exits. SysThreadRemoveExitCallback()
gets rid of an exit callback that was previously installed for the thread.
SysThreadChangePriority()
changes the scheduling priority of a thread. You must supply the ID of the thread being changed. Note that the ID of the current thread can be determined by calling SysCurrentThread()
.
Thread Groups
There are three functions for creating and managing thread groups. Thread groups are a convenience provided by the operating system that allows you to wait for one or more threads to exit. Thread groups are useful for unloading libraries that have spawned their own threads. Note that destroying a thread group implicitly waits for all threads in that group to exit.
SysThreadGroupCreate()
creates a new thread group. When you create the thread group, you must specify all of the threads that are to be a member of the group. Another thread can then call SysThreadGroupWait()
to block on the threads in the thread group; once all of the threads in the group have exited, the thread waiting on the thread group will resume.
You can destroy a thread group with SysThreadGroupDestroy()
. Note that this function waits until all of the thread group's threads have exited.
Inter-Process Communication (IPC)
There are a number of ways in which process can communicate with each other in Palm OS Cobalt.
IPC with Shared Memory
The traditional Palm OS Feature Manager and Data Manager APIs are available to all threads in the system. Because these use a common memory area, they can be used for simple IPC operations. These are a very limited solution, however, and won't meet the needs of many developers, particularly developers of enterprise applications.
IPC with Named Pipes
The IOS subsystem in Palm OS Cobalt includes a facility called named pipes that can be used to transfer data across processes. This is accomplished by calling IOSPipe()
from within one process to create the pipe and then publishing it under a specific name with IOSFattach()
. At this point, a thread in any other process can open the pipe as a normal device using the IOSOpen()
call and start performing I/O on it using IOSRead()
, IOSWrite()
, and other IOS functions.
Applications can send IOS file descriptors through these pipes. You cannot, however, transfer other system objects such as semaphores, memory segments, and the like.
Communication in Same Process
Event queues are a simple mechanism that allow you to easily communicate between threads in the same process. Each thread has an event queue; a thread can obtain a handle to its event queue by calling EvtGetThreadEventQueue()
. Be sure to release this handle once you no longer need it by calling EvtReleaseEventQueue()
.
EvtAddEventToQueue()
sends an event to the currently running application. Because "current application" is always a moving target, the receiver of this API is not always clear, so this function is not generally useful. It does, however, provide at least one channel of communication from the Background Process to the Application process. Applications will likely want to use EvtAddEventToEventQueue()
instead. This function sends an event to a specific event queue, identified by the event queue's handle. It also allows you to specify a "reply queue" to which replies can be posted. Often you'll want the calling thread's queue to be the reply queue—the thread handle returned from EvtGetThreadEventQueue()
will serve this purpose.
Communicating with the Background Process
Event queues can also be used when communicating across process boundaries. EvtCreateBackgroundThread()
creates a thread in the Background Process and returns a communication path back to the caller through which events can be sent. This allows the original application to control the background thread by sending it event codes. An optional parameter to this function, callerQueue
, allows the caller to pass to the background thread an event queue that the background thread can use to pass events back to the caller. To have the background thread post replies back to the calling thread's queue, supply the value returned from EvtGetThreadEventQueue()
as the callerQueue
parameter.
From within the background thread, you then obtain the caller's queue with EvtGetReplyEventQueue()
.
Attaching to a Running Background Thread
For the case where an application needs to attach to its already running background thread whenever it starts, use the following functions to publish and then retrieve the background process' event queue by name:
Atomic Operations
There are a variety of atomic operation APIs that are useful for applications. SysAtomicAdd32()
, SysAtomicAnd32()
, SysAtomicOr32()
perform an atomic operation of the given type on a 32-bit quantity. SysAtomicCompareAndSwap32()
atomically changes a 32-bit value to a new arbitrary value, but only if the current value is the same as one provided.
Synchronization
Palm OS Cobalt provides wrappers for the lightweight critical section and condition variable primitives discussed under "Lightweight Synchronization Primitives." The critical section APIs consist of two functions, SysCriticalSectionEnter()
and SysCriticalSectionExit()
, that allow you to acquire and release a critical section, respectively.
The condition variable functions include:
-
SysConditionVariableWait()
(which can optionally exit and enter a critical section when blocking) -
SysConditionVariableClose()
-
SysConditionVariableOpen()
-
SysConditionVariableBroadcast()
The above functions do not require creation and destruction functions, as the objects themselves are simple 32-bit values that are initialized to a well-defined constant: sysCriticalSectionInitializer
in the case of a critical section, and sysConditionVariableInitializer
in the case of a condition variable.
Thread-Specific Data
There is a set of functions in SysThread.h
for working with thread-specific data (TSD):
-
SysTSDAllocate()
allocates a new TSD slot. You can supply an optional destructor function which is called whenever a thread exits to clean up any data associated with the slot. You can also specify a name; multiple calls toSysTSDAllocate()
with the same slot name results in the same slot being returned instead of a new one being allocated each time. -
SysTSDFree()
deallocates a previously created TSD slot.
-
SysTSDGet()
returns the data in a particular slot. -
SysTSDSet()
changes the data in a particular slot.
Accessing the User Interface from Outside the Main UI Thread
With just two functions, it is possible for background threads to bring up their own UI, which greatly increases what can be done in them. For example, a background thread could present a progress dialog.
Window.h
declares functions for performing UI operations outside of the main UI thread: WinStartThreadUI()
and WinFinishThreadUI()
. These functions control the lifetime of the calling thread's graphics context. You can nest calls to these functions, so you can use them in a function to ensure that the current thread has a graphics context while inside that function, whether or not there is one from the outside caller.
Once a thread that is not the main UI thread has been bound to a graphics context, it can make use of almost all of the Palm OS UI APIs. There are, however, some limitations placed on them. The most important is that these windows can only be update-based; they do not support back buffering. This carries with it all of the other implications of update-based windows, which are discussed in Exploring Palm OS: User Interface. One additional limitation is that these threads cannot call IOSPoll()
for multiplexing UI operations with I/O. Instead, they are expected to spawn additional threads for handling the I/O and can use the new Event Manager APIs described earlier to interact with the UI thread.