//
// Copyright 2005 The Android Open Source Project
//
#ifndef ANDROID_SIGNAL_HANDLER_H
#define ANDROID_SIGNAL_HANDLER_H
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <signal.h>
namespace android {
// ----------------------------------------------------------------------
enum {
DEFAULT_PROCESS_TAG = 1
};
class SignalHandler
{
public:
typedef void (*child_callback_t)(pid_t child, void* userData);
/**
* Set a handler for when a child process exits. By calling
* this, a waitpid() will be done when the child exits to remove
* it from the zombie state. You can also optionally specify a
* handler to be called when the child exits.
*
* If there is already a handler for this child process, it is
* replaced by this new handler. In this case the old handler's
* function is not called.
*
* @param childPid Process ID of child to watch.
* @param childTag User-defined tag for this child. Must be
* greater than zero.
* @param handler If non-NULL, this will be called when the
* child exits. It may be called in either a
* separate signal handling thread, or
* immediately if the child has already exited.
* @param userData Propageted as-is to handler.
*
* @return status_t NO_ERROR if all is well.
*/
static status_t setChildHandler(pid_t childPid,
int childTag = DEFAULT_PROCESS_TAG,
child_callback_t handler = NULL,
void* userData = NULL);
/**
* Kill all of the child processes for which we have a waiting
* handler, whose tag is the given value. If tag is 0, all
* children are killed.
*
* @param tag
*/
static void killAllChildren(int tag = 0);
private:
SignalHandler();
~SignalHandler();
static SignalHandler* getInstance();
static void sigAction(int, siginfo_t*, void*);
// --------------------------------------------------
// Shared state... all of this is protected by mLock.
// --------------------------------------------------
mutable Mutex mLock;
struct ChildHandler
{
pid_t childPid;
int tag;
child_callback_t handler;
void* userData;
};
KeyedVector<pid_t, ChildHandler> mChildHandlers;
// --------------------------------------------------
// Commmand queue... data is inserted by the signal
// handler using atomic ops, and retrieved by the
// signal processing thread. Because these are touched
// by the signal handler, no lock is used.
// --------------------------------------------------
enum {
COMMAND_QUEUE_SIZE = 64
};
struct CommandEntry
{
int filled;
int signum;
siginfo_t info;
};
// The top of the queue. This is incremented atomically by the
// signal handler before placing a command in the queue.
volatile int32_t mCommandTop;
// The bottom of the queue. Only modified by the processing
// thread; the signal handler reads it only to determine if the
// queue is full.
int32_t mCommandBottom;
// Incremented each time we receive a signal and don't have room
// for it on the command queue.
volatile int32_t mLostCommands;
// The command processing thread.
class ProcessThread;
sp<Thread> mProcessThread;
// Pipe used to tell command processing thread when new commands.
// are available. The thread blocks on the read end, the signal
// handler writes when it enqueues new commands.
int mAvailMsg[2];
// The commands.
CommandEntry mCommands[COMMAND_QUEUE_SIZE];
// --------------------------------------------------
// Singleton.
// --------------------------------------------------
static Mutex mInstanceLock;
static SignalHandler* mInstance;
};
// ----------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_SIGNAL_HANDLER_H