blob: cccaabfc57ad4d92d1b6a7c841e55973361dde86 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001//
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