Intertask and Interprocess Communication
SIGEV_THREAD
Provides for callback functions for asynchronous notifications done by a function call within the context of a new thread. This provides a
multi-threaded process with a more natural means of notification than signals.
VxWorks supports this option in user space (processes), but not in the kernel.
The notification type is specified using the sigevent structure, which is defined in installDir/vxworks-6.x/target/h/sigeventCommon.h. A pointer the structure is used in the call to register for signal notification; for example, with mq_notify( ).
To use the signal event facility, configure VxWorks with the INCLUDE_SIGEVENT component.
As noted above, the SIGEV_THREAD option is only supported in processes, and it requires that VxWorks be configured with the INCLUDE_SIGEVENTS_THREAD component and full POSIX thread support (the BUNDLE_RTP_POSIX_PSE52 bundle includes everything required for this option).
8.2.5 Signal Handlers
Signals are more appropriate for error and exception handling than as a general-purpose intertask communication mechanism. And normally, signal handlers should be treated like ISRs: no routine should be called from a signal handler that might cause the handler to block. Because signals are asynchronous, it is difficult to predict which resources might be unavailable when a particular signal is raised.
To be perfectly safe, call only those routines listed in Table 8-4. Deviate from this practice only if you are certain that your signal handler cannot create a deadlock situation.
In addition, you should be particularly careful when using C++ for a signal handler or when invoking a C++ method from a signal handler written in C or assembly. Some of the issues involved in using C++ include the following:
■ The VxWorks intConnect( ) and signal( ) routines require the address of the function to execute when the interrupt or signal occurs, but the address of a non-static member function cannot be used, so static member functions must be implemented.
■ Objects cannot be instantiated or deleted in signal handling code.
■ C++ code used to execute in a signal handler should restrict itself to Embedded C++. No exceptions nor run-time type identification (RTTI) should be used.
Most signals are delivered asynchronously to the execution of a program.
Therefore programs must be written to account for the unexpected occurrence of signals, and handle them gracefully. Unlike ISR's, signal handlers execute in the context of the interrupted task.
VxWorks does not distinguish between normal task execution and a signal context, as it distinguishes between a task context and an ISR. Therefore the system has no way of distinguishing between a task execution context and a task
executing a signal handler. To the system, they are the same.
When you write signal handlers make sure that they:
■ Release resources prior to exiting:
– Free any allocated memory.
– Close any open files.
– Release any mutual exclusion resources such as semaphores.
■ Leave any modified data structures in a sane state.
Notify the parent process with an appropriate error return value. Mutual exclusion between signal handlers and tasks must be managed with care. In general, users should avoid the following activity in signal handlers:
■ Taking mutual exclusion (such as semaphores) resources that can also be taken by any other element of the application code. This can lead to deadlock.
Table 8-4 Routines Callable by Signal Handlers
Library Routines bLib All routines
errnoLib errnoGet( ), errnoSet( ) eventLib eventSend( )
logLib logMsg( )
lstLib All routines except lstFree( ) msgQLib msgQSend( )
rngLib All routines except rngCreate( ) and rngDelete( )
semLib semGive( ) except mutual-exclusion semaphores, semFlush( ) sigLib kill( )
taskLib taskSuspend( ), taskResume( ), taskPrioritySet( ), taskPriorityGet( ), taskIdVerify( ), taskIdDefault( ), taskIsReady( ), taskIsSuspended( ), taskIsPended( ), taskIsDelayed( ), taskTcb( )
tickLib tickAnnounce( ), tickSet( ), tickGet( )
8 Signals 8.2 Signals
■ Modifying any shared data memory that may have been in the process of modification by any other element of the application code when the signal was delivered. This compromises mutual exclusion and leads to data corruption.
■ Using longjmp( ) to change the flow of task execution. If longjmp( ) is used in a signal handler to re-initialize a running task, you must ensure that the signal is not sent to the task while the task is holding a critical resource (such as a kernel mutex). For example, if a signal is sent to a task that is executing malloc( ), the signal handler that calls longjmp( ) could leave the kernel in an inconsistent state.
These scenarios are very difficult to debug, and should be avoided. One safe way to synchronize other elements of the application code and a signal handler is to set up dedicated flags and data structures that are set from signal handlers and read from the other elements. This ensures a consistency in usage of the data structure.
In addition, the other elements of the application code must check for the occurrence of signals at any time by periodically checking to see if the
synchronizing data structure or flag has been modified in the background by a signal handler, and then acting accordingly. The use of the volatile keyword is useful for memory locations that are accessed from both a signal handler and other elements of the application.
Taking a mutex semaphore in a signal handler is an especially bad idea. Mutex semaphores can be taken recursively. A signal handler can therefore easily re-acquire a mutex that was taken by any other element of the application. Since the signal handler is an asynchronously executing entity, it has thereby broken the mutual exclusion that the mutex was supposed to provide.
Taking a binary semaphore in a signal handler is an equally bad idea. If any other element has already taken it, the signal handler will cause the task to block on itself. This is a deadlock from which no recovery is possible. Counting semaphores, if available, suffer from the same issue as mutexes, and if unavailable, are equivalent to the binary semaphore situation that causes an unrecoverable deadlock.
On a general note, the signal facility should be used only for notifying/handling exceptional or error conditions. Usage of signals as a general purpose IPC mechanism or in the data flow path of an application can cause some of the pitfalls described above.