250 lines
6.6 KiB
C++
250 lines
6.6 KiB
C++
//
|
|
// 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
|
|
|