Signals

The OS supports the 32 standard POSIX signals (as in UNIX) as well as the POSIX realtime signals, both numbered from a kernel-implemented set of 64 signals with uniform functionality. While the POSIX standard defines realtime signals as differing from UNIX-style signals (in that they may contain four bytes of data and a byte code and may be queued for delivery), this functionality can be explicitly selected or deselected on a per-signal basis, allowing this converged implementation to still comply with the standard. Incidentally, the UNIX-style signals can select POSIX realtime signal queuing, if the application wants it. The BlackBerry 10 OS also extends the signal-delivery mechanisms of POSIX by allowing signals to be targeted at specific threads, rather than simply at the process containing the threads. Since signals are an asynchronous event, they're also implemented with the event-delivery mechanisms.

Microkernel call POSIX call Description
SignalKill() kill(), pthread_kill(), raise(), sigqueue() Set a signal on a process group, process, or thread.
SignalAction() sigaction() Define action to take on receipt of a signal.
SignalProcmask() sigprocmask(), pthread_sigmask() Change signal blocked mask of a thread.
SignalSuspend() sigsuspend(), pause() Block until a signal invokes a signal handler.
SignalWaitinfo() sigwaitinfo() Wait for signal and return info on it.

The original POSIX specification defined signal operation on processes only. In a multithreaded process, the following rules are followed:

  • The signal actions are maintained at the process level. If a thread ignores or catches a signal, it affects all threads within the process.
  • The signal mask is maintained at the thread level. If a thread blocks a signal, it affects only that thread.
  • An unignored signal targeted at a thread is delivered to that thread alone.
  • An unignored signal targeted at a process is delivered to the first thread that doesn't have the signal blocked. If all threads have the signal blocked, the signal is queued on the process until any thread ignores or unblocks the signal. If ignored, the signal on the process is removed. If unblocked, the signal is moved from the process to the thread that unblocked it.

When a signal is targeted at a process with a large number of threads, the thread table must be scanned, looking for a thread with the signal unblocked. Standard practice for most multithreaded processes is to mask the signal in all threads but one, which is dedicated to handling them. To increase the efficiency of process-signal delivery, the kernel caches the last thread that accepted a signal and always attempts to deliver the signal to it first.

Diagram showing BlackBerry 10 OS signal delivery.

The POSIX standard includes the concept of queued realtime signals. The BlackBerry 10 OS supports optional queuing of any signal, not just realtime signals. The queuing can be specified on a signal-by-signal basis within a process. Each signal can have an associated 8-bit code and a 32-bit value.

This is very similar to message pulses described earlier. The kernel takes advantage of this similarity and uses common code for managing both signals and pulses. The signal number is mapped to a pulse priority using _SIGMAXsigno. As a result, signals are delivered in priority order with lower signal numbers having higher priority. This conforms with the POSIX standard, which states that existing signals have priority over the new realtime signals.

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

Special signals

The OS defines a total of 64 signals. Their range is as follows:

Signal range Description
1 ... 57 57 POSIX signals (including traditional UNIX signals)
41 ... 56 16 POSIX realtime signals (SIGRTMIN to SIGRTMAX)
57 ... 64 Eight special-purpose BlackBerry 10 OS signals

The eight special signals cannot be ignored or caught. An attempt to call the signal() or sigaction() functions or the SignalAction() kernel call to change them fails with an error of EINVAL.

In addition, these signals are always blocked and have signal queuing enabled. An attempt to unblock these signals via the sigprocmask() function or SignalProcmask() kernel call is quietly ignored.

A regular signal can be programmed to this behavior using the following standard signal calls. The special signals save the programmer from writing this code and protect the signal from accidental changes to this behavior.

sigset_t *set;
struct sigaction action;

sigemptyset(&set);
sigaddset(&set, signo);
sigprocmask(SIG_BLOCK, &set, NULL);

action.sa_handler = SIG_DFL;
action.sa_flags = SA_SIGINFO;
sigaction(signo, &action, NULL);

This configuration makes these signals suitable for synchronous notification using the sigwaitinfo() function or SignalWaitinfo() kernel call. The following code blocks until the eighth special signal is received:

sigset_t *set;
siginfo_t info;

sigemptyset(&set);
sigaddset(&set, SIGRTMAX + 8);
sigwaitinfo(&set, &info);
printf("Received signal %d with code %d and value %d\n",
            info.si_signo,
            info.si_code,
            info.si_value.sival_int);

Since the signals are always blocked, the program cannot be interrupted or killed if the special signal is delivered outside of the sigwaitinfo() function. Since signal queuing is always enabled, signals won't be lost—they are queued for the next sigwaitinfo() call.

These signals were designed to solve a common IPC requirement where a server wishes to notify a client that it has information available for the client. The server uses the MsgDeliverEvent() call to notify the client. There are two reasonable choices for the event within the notification: pulses or signals.

A pulse is the preferred method for a client that may also be a server to other clients. In this case, the client has created a channel for receiving messages and can also receive the pulse.

This won't be true for most simple clients. To receive a pulse, a simple client would be forced to create a channel for this express purpose. A signal can be used in place of a pulse if the signal is configured to be synchronous (that is, the signal is blocked) and queued—this is exactly how the special signals are configured. The client would replace the MsgReceive() call used to wait for a pulse on a channel with a simple sigwaitinfo() call to wait for the signal.

The eight special signals include named signals for special purposes:

SIGSELECT
Used by select() to wait for I/O from multiple servers.

Summary of signals

This table describes what each signal means.

Signal Description
SIGABRT Abnormal termination signal such as issued by the abort() function.
SIGALRM Timeout signal such as issued by the alarm() function.
SIGBUS Indicates a memory parity error (BlackBerry 10 OS-specific interpretation). Note that if a second fault occurs while your process is in a signal handler for this fault, the process is terminated.
SIGCHLD (or SIGCLD) Child process terminated. The default action is to ignore the signal.
SIGCONT Continue if HELD. The default action is to ignore the signal if the process isn't HELD.
SIGDEADLK Mutex deadlock occurred. If a process dies while holding a mutex, and you haven't called SyncMutexEvent() to set up an event to be delivered to the mutex's owner when the mutex dies, the kernel delivers a SIGDEADLK instead to all threads that are waiting on the mutex without a timeout.

Note that SIGDEADLK uses the same signal number as SIGEMT. Some utilities (for example, gdb , ksh , and slay ) know about SIGEMT, but not SIGDEADLCK.

SIGEMT EMT instruction (emulator trap).

Note that SIGEMT uses the same signal number as SIGDEADLK.

SIGFPE Erroneous arithmetic operation (integer or floating point), such as division by zero or an operation resulting in overflow. Note that if a second fault occurs while your process is in a signal handler for this fault, the process is terminated.
SIGHUP Death of session leader, or hangup detected on controlling terminal.
SIGILL Detection of an invalid hardware instruction. Note that if a second fault occurs while your process is in a signal handler for this fault, the process is terminated.

One possible cause for this signal is trying to perform an operation that requires I/O privileges. A thread can request these privileges by:

  1. Enabling the PROCMGR_AID_IO ability enabled. For more information, see procmgr_ability().
  2. Calling ThreadCtl(), specifying the _NTO_TCTL_IO flag:
    ThreadCtl( _NTO_TCTL_IO, 0 );
          
SIGINT Interactive attention signal (Break).
SIGIOT IOT instruction (not generated on x86 hardware).
SIGKILL Termination signal—should be used only for emergency situations. This signal can't be caught or ignored.
SIGPIPE Attempt to write on a pipe with no readers.
SIGPOLL (or SIGIO) Pollable event occurred.
SIGPWR Power failure or restart.
SIGQUIT Interactive termination signal.
SIGSEGV Detection of an invalid memory reference. Note that if a second fault occurs while your process is in a signal handler for this fault, the process is terminated.
SIGSTOP Stop process (the default). This signal cannot be caught or ignored.
SIGSYS Bad argument to system call.
SIGTERM Termination signal.
SIGTRAP Unsupported software interrupt.
SIGTSTP Stop signal generated from keyboard.
SIGTTIN Background read attempted from control terminal.
SIGTTOU Background write attempted to control terminal.
SIGURG Urgent condition present on socket.
SIGUSR1 Reserved as application-defined signal 1.
SIGUSR2 Reserved as application-defined signal 2.
SIGWINCH Window size changed.

Last modified: 2015-05-07



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

comments powered by Disqus