summaryrefslogtreecommitdiffstats
path: root/cmds/runtime/SignalHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/runtime/SignalHandler.cpp')
-rw-r--r--cmds/runtime/SignalHandler.cpp249
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
+