Home | History | Annotate | Download | only in runtime
      1 //
      2 // Copyright 2005 The Android Open Source Project
      3 //
      4 
      5 #define LOG_TAG "SignalHandler"
      6 
      7 #include "SignalHandler.h"
      8 
      9 #include <utils/Atomic.h>
     10 #include <utils/Debug.h>
     11 #include <utils/Log.h>
     12 
     13 #include <errno.h>
     14 #include <sys/wait.h>
     15 #include <unistd.h>
     16 
     17 namespace android {
     18 
     19 class SignalHandler::ProcessThread : public Thread
     20 {
     21 public:
     22     ProcessThread(SignalHandler& sh)
     23         : Thread(false)
     24         , mOwner(sh)
     25     {
     26     }
     27 
     28     virtual bool threadLoop()
     29     {
     30         char buffer[32];
     31         read(mOwner.mAvailMsg[0], buffer, sizeof(buffer));
     32 
     33         LOGV("Signal command processing thread woke up!");
     34 
     35         if (mOwner.mLostCommands) {
     36             LOGE("Lost %d signals!", mOwner.mLostCommands);
     37             mOwner.mLostCommands = 0;
     38         }
     39 
     40         int cur;
     41         while ((cur=mOwner.mCommandBottom) != mOwner.mCommandTop) {
     42             if (mOwner.mCommands[cur].filled == 0) {
     43                 LOGV("Command at %d is not yet filled", cur);
     44                 break;
     45             }
     46 
     47             LOGV("Processing command at %d, top is %d",
     48                  cur, mOwner.mCommandTop);
     49             processCommand(mOwner.mCommands[cur]);
     50             mOwner.mCommands[cur].filled = 0;
     51 
     52             int next = mOwner.mCommandBottom+1;
     53             if (next >= COMMAND_QUEUE_SIZE) {
     54                 next = 0;
     55             }
     56 
     57             mOwner.mCommandBottom = next;
     58         }
     59 
     60         return true;
     61     }
     62 
     63     void processCommand(const CommandEntry& entry)
     64     {
     65         switch (entry.signum) {
     66         case SIGCHLD: {
     67             mOwner.mLock.lock();
     68             ssize_t i = mOwner.mChildHandlers.indexOfKey(entry.info.si_pid);
     69             ChildHandler ch;
     70             if (i >= 0) {
     71                 ch = mOwner.mChildHandlers.valueAt(i);
     72                 mOwner.mChildHandlers.removeItemsAt(i);
     73             }
     74             mOwner.mLock.unlock();
     75 
     76             LOGD("SIGCHLD: pid=%d, handle index=%d", entry.info.si_pid, i);
     77 
     78             if (i >= 0) {
     79                 int res = waitpid(entry.info.si_pid, NULL, WNOHANG);
     80                 LOGW_IF(res == 0,
     81                         "Received SIGCHLD, but pid %d is not yet stopped",
     82                         entry.info.si_pid);
     83                 if (ch.handler) {
     84                     ch.handler(entry.info.si_pid, ch.userData);
     85                 }
     86             } else {
     87                 LOGW("Unhandled SIGCHLD for pid %d", entry.info.si_pid);
     88             }
     89         } break;
     90         }
     91     }
     92 
     93     SignalHandler& mOwner;
     94 };
     95 
     96 
     97 Mutex SignalHandler::mInstanceLock;
     98 SignalHandler* SignalHandler::mInstance = NULL;
     99 
    100 status_t SignalHandler::setChildHandler(pid_t childPid,
    101                                         int tag,
    102                                         child_callback_t handler,
    103                                         void* userData)
    104 {
    105     SignalHandler* const self = getInstance();
    106 
    107     self->mLock.lock();
    108 
    109     // First make sure this child hasn't already exited.
    110     pid_t res = waitpid(childPid, NULL, WNOHANG);
    111     if (res != 0) {
    112         if (res < 0) {
    113             LOGW("setChildHandler waitpid of %d failed: %d (%s)",
    114                  childPid, res, strerror(errno));
    115         } else {
    116             LOGW("setChildHandler waitpid of %d said %d already dead",
    117                  childPid, res);
    118         }
    119 
    120         // Some kind of error...  just handle the exit now.
    121         self->mLock.unlock();
    122 
    123         if (handler) {
    124             handler(childPid, userData);
    125         }
    126 
    127         // Return an error code -- 0 means it already exited.
    128         return (status_t)res;
    129     }
    130 
    131     ChildHandler entry;
    132     entry.childPid = childPid;
    133     entry.tag = tag;
    134     entry.handler = handler;
    135     entry.userData = userData;
    136 
    137     // Note: this replaces an existing entry for this pid, if there already
    138     // is one.  This is the required behavior.
    139     LOGD("setChildHandler adding pid %d, tag %d, handler %p, data %p",
    140          childPid, tag, handler, userData);
    141     self->mChildHandlers.add(childPid, entry);
    142 
    143     self->mLock.unlock();
    144 
    145     return NO_ERROR;
    146 }
    147 
    148 void SignalHandler::killAllChildren(int tag)
    149 {
    150     SignalHandler* const self = getInstance();
    151 
    152     AutoMutex _l (self->mLock);
    153     const size_t N = self->mChildHandlers.size();
    154     for (size_t i=0; i<N; i++) {
    155         const ChildHandler& ch(self->mChildHandlers.valueAt(i));
    156         if (tag == 0 || ch.tag == tag) {
    157             const pid_t pid = ch.childPid;
    158             LOGI("Killing child %d (tag %d)\n", pid, ch.tag);
    159             kill(pid, SIGKILL);
    160         }
    161     }
    162 }
    163 
    164 SignalHandler::SignalHandler()
    165     : mCommandTop(0)
    166     , mCommandBottom(0)
    167     , mLostCommands(0)
    168 {
    169     memset(mCommands, 0, sizeof(mCommands));
    170 
    171     int res = pipe(mAvailMsg);
    172     LOGE_IF(res != 0, "Unable to create signal handler pipe: %s", strerror(errno));
    173 
    174     mProcessThread = new ProcessThread(*this);
    175     mProcessThread->run("SignalHandler", PRIORITY_HIGHEST);
    176 
    177     struct sigaction sa;
    178     memset(&sa, 0, sizeof(sa));
    179     sa.sa_sigaction = sigAction;
    180     sa.sa_flags = SA_NOCLDSTOP|SA_SIGINFO;
    181     sigaction(SIGCHLD, &sa, NULL);
    182 }
    183 
    184 SignalHandler::~SignalHandler()
    185 {
    186 }
    187 
    188 SignalHandler* SignalHandler::getInstance()
    189 {
    190     AutoMutex _l(mInstanceLock);
    191     if (mInstance == NULL) {
    192         mInstance = new SignalHandler();
    193     }
    194     return mInstance;
    195 }
    196 
    197 void SignalHandler::sigAction(int signum, siginfo_t* info, void*)
    198 {
    199     static const char wakeupMsg[1] = { 0xff };
    200 
    201     // If our signal handler is being called, then we know we have
    202     // already initialized the SignalHandler class and thus mInstance
    203     // is valid.
    204     SignalHandler* const self = mInstance;
    205 
    206     // XXX This is not safe!
    207     #if 0
    208     LOGV("Signal %d: signo=%d, errno=%d, code=%d, pid=%d\n",
    209            signum,
    210            info->si_signo, info->si_errno, info->si_code,
    211            info->si_pid);
    212     #endif
    213 
    214     int32_t oldTop, newTop;
    215 
    216     // Find the next command slot...
    217     do {
    218         oldTop = self->mCommandTop;
    219 
    220         newTop = oldTop + 1;
    221         if (newTop >= COMMAND_QUEUE_SIZE) {
    222             newTop = 0;
    223         }
    224 
    225         if (newTop == self->mCommandBottom) {
    226             // The buffer is filled up!  Ouch!
    227             // XXX This is not safe!
    228             #if 0
    229             LOGE("Command buffer overflow!  newTop=%d\n", newTop);
    230             #endif
    231             android_atomic_add(1, &self->mLostCommands);
    232             write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg));
    233             return;
    234         }
    235     } while(android_atomic_cmpxchg(oldTop, newTop, &(self->mCommandTop)));
    236 
    237     // Fill in the command data...
    238     self->mCommands[oldTop].signum = signum;
    239     self->mCommands[oldTop].info = *info;
    240 
    241     // And now make this command available.
    242     self->mCommands[oldTop].filled = 1;
    243 
    244     // Wake up the processing thread.
    245     write(self->mAvailMsg[1], wakeupMsg, sizeof(wakeupMsg));
    246 }
    247 
    248 }; // namespace android
    249 
    250