blob: cccaabfc57ad4d92d1b6a7c841e55973361dde86 [file] [log] [blame]
//
// Copyright 2005 The Android Open Source Project
//
#define LOG_TAG "SignalHandler"
#include "SignalHandler.h"
#include <utils/Atomic.h>
#include <utils/Debug.h>
#include <utils/Log.h>
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
namespace android {
class SignalHandler::ProcessThread : public Thread
{
public:
ProcessThread(SignalHandler& sh)
: Thread(false)
, mOwner(sh)
{
}
virtual bool threadLoop()
{
char buffer[32];
read(mOwner.mAvailMsg[0], buffer, sizeof(buffer));
LOGV("Signal command processing thread woke up!");
if (mOwner.mLostCommands) {
LOGE("Lost %d signals!", mOwner.mLostCommands);
mOwner.mLostCommands = 0;
}
int cur;
while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) {
if (mOwner.mCommands[cur].filled == 0) {
LOGV("Command at %d is not yet filled", cur);
break;
}
LOGV("Processing command at %d, top is %d",
cur, mOwner.mCommandTop);
processCommand(mOwner.mCommands[cur]);
mOwner.mCommands[cur].filled = 0;
int next = mOwner.mCommandBottom+1;
if (next >= COMMAND_QUEUE_SIZE) {
next = 0;
}
mOwner.mCommandBottom = next;
}
return true;
}
void processCommand(const CommandEntry& entry)
{
switch (entry.signum) {
case SIGCHLD: {
mOwner.mLock.lock();
ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid);
ChildHandler ch;
if (i >= 0) {
ch = mOwner.mChildHandlers.valueAt(i);
mOwner.mChildHandlers.removeItemsAt(i);
}
mOwner.mLock.unlock();
LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i);
if (i >= 0) {
int res = waitpid(entry.info.si_pid, NULL, WNOHANG);
LOGW_IF(res == 0,
"Received SIGCHLD, but pid %d is not yet stopped",
entry.info.si_pid);
if (ch.handler) {
ch.handler(entry.info.si_pid, ch.userData);
}
} else {
LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid);
}
} break;
}
}
SignalHandler& mOwner;
};
Mutex SignalHandler::mInstanceLock;
SignalHandler* SignalHandler::mInstance = NULL;
status_t SignalHandler::setChildHandler(pid_t childPid,
int tag,
child_callback_t handler,
void* userData)
{
SignalHandler* const self = getInstance();
self->mLock.lock();
// First make sure this child hasn't already exited.
pid_t res = waitpid(childPid, NULL, WNOHANG);
if (res != 0) {
if (res < 0) {
LOGW("setChildHandler waitpid of %d failed: %d (%s)",
childPid, res, strerror(errno));
} else {
LOGW("setChildHandler waitpid of %d said %d already dead",
childPid, res);
}
// Some kind of error... just handle the exit now.
self->mLock.unlock();
if (handler) {
handler(childPid, userData);
}
// Return an error code -- 0 means it already exited.
return (status_t)res;
}
ChildHandler entry;
entry.childPid = childPid;
entry.tag = tag;
entry.handler = handler;
entry.userData = userData;
// Note: this replaces an existing entry for this pid, if there already
// is one. This is the required behavior.
LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p",
childPid, tag, handler, userData);
self->mChildHandlers.add(childPid, entry);
self->mLock.unlock();
return NO_ERROR;
}
void SignalHandler::killAllChildren(int tag)
{
SignalHandler* const self = getInstance();
AutoMutex _l (self->mLock);
const size_t N = self->mChildHandlers.size();
for (size_t i=0; i<N; i++) {
const ChildHandler& ch(self->mChildHandlers.valueAt(i));
if (tag == 0 || ch.tag == tag) {
const pid_t pid = ch.childPid;
LOGI("Killing child %d (tag %d)\n", pid, ch.tag);
kill(pid, SIGKILL);
}
}
}
SignalHandler::SignalHandler()
: mCommandTop(0)
, mCommandBottom(0)
, mLostCommands(0)
{
memset(mCommands, 0, sizeof(mCommands));
int res = pipe(mAvailMsg);
LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno));
mProcessThread = new ProcessThread(*this);
mProcessThread->run("SignalHandler", PRIORITY_HIGHEST);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sigAction;
sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO;
sigaction(SIGCHLD, &sa, NULL);
}
SignalHandler::~SignalHandler()
{
}
SignalHandler* SignalHandler::getInstance()
{
AutoMutex _l(mInstanceLock);
if (mInstance == NULL) {
mInstance = new SignalHandler();
}
return mInstance;
}
void SignalHandler::sigAction(int signum, siginfo_t* info, void*)
{
static const char wakeupMsg[1] = { 0xff };
// If our signal handler is being called, then we know we have
// already initialized the SignalHandler class and thus mInstance
// is valid.
SignalHandler* const self = mInstance;
// XXX This is not safe!
#if 0
LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n",
signum,
info->si_signo, info->si_errno, info->si_code,
info->si_pid);
#endif
int32_t oldTop, newTop;
// Find the next command slot...
do {
oldTop = self->mCommandTop;
newTop = oldTop + 1;
if (newTop >= COMMAND_QUEUE_SIZE) {
newTop = 0;
}
if (newTop == self->mCommandBottom) {
// The buffer is filled up! Ouch!
// XXX This is not safe!
#if 0
LOGE("Command buffer overflow! newTop=%d\n", newTop);
#endif
android_atomic_add(1, &self->mLostCommands);
write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg));
return;
}
} while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop)));
// Fill in the command data...
self->mCommands[oldTop].signum = signum;
self->mCommands[oldTop].info = *info;
// And now make this command available.
self->mCommands[oldTop].filled = 1;
// Wake up the processing thread.
write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg));
}
}; // namespace android