diff options
Diffstat (limited to 'cmds/runtime/SignalHandler.cpp')
-rw-r--r-- | cmds/runtime/SignalHandler.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/cmds/runtime/SignalHandler.cpp b/cmds/runtime/SignalHandler.cpp new file mode 100644 index 0000000..cccaabf --- /dev/null +++ b/cmds/runtime/SignalHandler.cpp @@ -0,0 +1,249 @@ +// +// 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 + |