Programming overview

The modular architecture is apparent throughout the entire system: the BlackBerry 10 OS itself consists of a set of cooperating processes, as does an application. Each individual process can comprise several cooperating threads. What keeps everything together is the priority-based preemptive scheduling in BlackBerry 10 OS, which ensures that time-critical tasks are dealt with by the right thread or process at the right time.

Process model

The BlackBerry 10 OS architecture consists of the QNX Neutrino microkernel and some number of cooperating processes. These processes communicate with each other via various forms of interprocess communication (IPC). Message passing is the primary form of IPC in BlackBerry 10 OS.

Figure showing software bus with IPC.

The above diagram shows the graphics driver sending a message to the font manager when it wants the bitmap for a font. The font manager responds with the bitmap.

An application as a set of processes

This idea of using a set of cooperating processes isn't limited to the OS system processes. Your applications should be written in exactly the same way. You might have some driver process that gathers data from some hardware and then needs to pass that data on to other processes, which then act on that data.

Let's use the example of an application that's monitoring the level of water in a reservoir. Should the water level rise too high, then you'll want to alert an operator as well as open some flow-control valve.

In terms of hardware, you'll have some water-level sensor tied to an I/O board in a computer. If the sensor detects some water, it will cause the I/O board to generate an interrupt.

The software consists of a driver process that talks to the I/O board and contains an interrupt handler to deal with the board's interrupt. You'll also have a GUI process that will display an alarm window when told to do so by the driver, and finally, another driver process that will open/close the flow-control valve.

Why break this application into multiple processes? Why not have everything done in one process? There are several reasons:

  1. Each process lives in its own protected memory space. If there's a bug such that a pointer has a value that isn't valid for the process, then when the pointer is next used, the hardware will generate a fault, which the kernel handles (the kernel will set the SIGSEGV signal on the process).

    This approach has two benefits. The first is that a stray pointer won't cause one process to overwrite the memory of another process. The implications are that one process can go bad while other processes keep running.

    The second benefit is that the fault will occur precisely when the pointer is used, not when it's overwriting some other process's memory. If a pointer were allowed to overwrite another process's memory, then the problem wouldn't manifest itself until later and would therefore be much harder to debug.

  2. It's very easy to add or remove processes from an application as need be. This implies that applications can be made scalable — adding new features is simply a matter of adding processes.
  3. Processes can be started and stopped on the fly, which comes in handy for dynamic upgrading or simply for stopping an offending process.
  4. Processing can be easily distributed across multiple processors in a networked environment.
  5. The code for a process is much simpler if it concentrates on doing a single job. For example, a single process that acts as a driver, a GUI front-end, and a data logger would be fairly complex to build and maintain. This complexity would increase the chances of a bug, and any such bug would likely affect all the activities being done by the process.
  6. Different programmers can work on different processes without fear of overwriting each other's work.

Some definitions

Different operating systems often have different meanings for terms such as process, thread, task, program, and so on. In the BlackBerry 10 OS, we typically use only the terms process and thread. An application typically means a collection of processes; the term program is usually equivalent to process.

A thread is a single flow of execution or control. At the lowest level, this equates to the program counter or instruction pointer register advancing through some machine instructions. Each thread has its own current value for this register.

A process is a collection of one or more threads that share many things. Threads within a process share at least the following:

  • Variables that aren't on the stack
  • Signal handlers (although you typically have one thread that handles signals, and you block them in all the other threads)

    It isn't safe to use floating-point operations in signal handlers.

  • Signal ignore mask
  • Channels
  • Connections

Threads don't share such things as stack, values for the various registers, SMP thread-affinity mask, and a few other things.

Two threads residing in two different processes don't share very much. About the only thing they do share is the CPU. You can have them share memory between them, but this takes a little setup (see shm_open() in the C Library Reference for an example).

When you run a process, you're automatically running a thread. This thread is called the main thread, since the first programmer-provided function that runs in a C program is main(). The main thread can then create additional threads if need be.

Only a few things are special about the main thread. One is that if it returns normally, the code it returns to calls exit(). Calling exit() terminates the process, meaning that all threads in the process are terminated. So when you return normally from the main thread, the process is terminated. When other threads in the process return normally, the code they return to calls pthread_exit(), which terminates just that thread.

Another special thing about the main thread is that if it terminates in such a manner that the process is still around (that is, it calls pthread_exit() and there are other threads in the process), then the memory for the main thread's stack is not freed up. This is because the command-line arguments are on that stack and other threads may need them. If any other thread terminates, then that thread's stack is freed.

Last modified: 2015-03-31

Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus