SignalHandler.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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
17namespace android {
18
19class SignalHandler::ProcessThread : public Thread
20{
21public:
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
97Mutex SignalHandler::mInstanceLock;
98SignalHandler* SignalHandler::mInstance = NULL;
99
100status_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
148void 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
164SignalHandler::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
184SignalHandler::~SignalHandler()
185{
186}
187
188SignalHandler* SignalHandler::getInstance()
189{
190    AutoMutex _l(mInstanceLock);
191    if (mInstance == NULL) {
192        mInstance = new SignalHandler();
193    }
194    return mInstance;
195}
196
197void 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