BPicture: Fix archive constructor.
[haiku.git] / src / kits / debug / DebugLooper.cpp
blob5dbc6f3d5ccc7122e9a372732af93e0c1aa8476d
1 /*
2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <DebugLooper.h>
9 #include <new>
11 #include <AutoLocker.h>
12 #include <DebugMessageHandler.h>
13 #include <TeamDebugger.h>
14 #include <util/DoublyLinkedList.h>
17 struct BDebugLooper::Debugger {
18 BTeamDebugger* debugger;
19 BDebugMessageHandler* handler;
21 Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler)
23 debugger(debugger),
24 handler(handler)
30 struct BDebugLooper::Job : DoublyLinkedListLinkImpl<Job> {
31 Job()
33 fDoneSemaphore(-1)
37 virtual ~Job()
41 status_t Wait(BLocker& lock)
43 fDoneSemaphore = create_sem(0, "debug looper job");
45 lock.Unlock();
47 while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) {
50 lock.Lock();
52 delete_sem(fDoneSemaphore);
53 fDoneSemaphore = -1;
55 return fResult;
58 void Done(status_t result)
60 fResult = result;
61 release_sem(fDoneSemaphore);
64 virtual status_t Do(BDebugLooper* looper) = 0;
66 protected:
67 sem_id fDoneSemaphore;
68 status_t fResult;
72 struct BDebugLooper::JobList : DoublyLinkedList<Job> {
76 struct BDebugLooper::AddDebuggerJob : Job {
77 AddDebuggerJob(BTeamDebugger* debugger,
78 BDebugMessageHandler* handler)
80 fDebugger(debugger),
81 fHandler(handler)
85 virtual status_t Do(BDebugLooper* looper)
87 Debugger* debugger = new(std::nothrow) Debugger(fDebugger, fHandler);
88 if (debugger == NULL || !looper->fDebuggers.AddItem(debugger)) {
89 delete debugger;
90 return B_NO_MEMORY;
93 return B_OK;
96 private:
97 BTeamDebugger* fDebugger;
98 BDebugMessageHandler* fHandler;
102 struct BDebugLooper::RemoveDebuggerJob : Job {
103 RemoveDebuggerJob(team_id team)
105 fTeam(team)
109 virtual status_t Do(BDebugLooper* looper)
111 for (int32 i = 0; Debugger* debugger = looper->fDebuggers.ItemAt(i);
112 i++) {
113 if (debugger->debugger->Team() == fTeam) {
114 delete looper->fDebuggers.RemoveItemAt(i);
115 return B_OK;
119 return B_ENTRY_NOT_FOUND;
122 private:
123 team_id fTeam;
127 // #pragma mark -
130 BDebugLooper::BDebugLooper()
132 fLock("debug looper"),
133 fThread(-1),
134 fOwnsThread(false),
135 fTerminating(false),
136 fNotified(false),
137 fJobs(NULL),
138 fEventSemaphore(-1)
143 BDebugLooper::~BDebugLooper()
148 status_t
149 BDebugLooper::Init()
151 status_t error = fLock.InitCheck();
152 if (error != B_OK)
153 return error;
155 AutoLocker<BLocker> locker(fLock);
157 if (fThread >= 0)
158 return B_BAD_VALUE;
160 if (fJobs == NULL) {
161 fJobs = new(std::nothrow) JobList;
162 if (fJobs == NULL)
163 return B_NO_MEMORY;
166 if (fEventSemaphore < 0) {
167 fEventSemaphore = create_sem(0, "debug looper event");
168 if (fEventSemaphore < 0)
169 return fEventSemaphore;
172 return B_OK;
176 thread_id
177 BDebugLooper::Run(bool spawnThread)
179 AutoLocker<BLocker> locker(fLock);
181 if (fThread >= 0)
182 return B_BAD_VALUE;
184 fNotified = false;
186 if (spawnThread) {
187 fThread = spawn_thread(&_MessageLoopEntry, "debug looper",
188 B_NORMAL_PRIORITY, this);
189 if (fThread < 0)
190 return fThread;
192 fOwnsThread = true;
194 resume_thread(fThread);
195 return B_OK;
198 fThread = find_thread(NULL);
199 fOwnsThread = false;
201 _MessageLoop();
202 return B_OK;
206 void
207 BDebugLooper::Quit()
209 AutoLocker<BLocker> locker(fLock);
211 fTerminating = true;
212 _Notify();
216 status_t
217 BDebugLooper::AddTeamDebugger(BTeamDebugger* debugger,
218 BDebugMessageHandler* handler)
220 if (debugger == NULL || handler == NULL)
221 return B_BAD_VALUE;
223 AddDebuggerJob job(debugger, handler);
224 return _DoJob(&job);
228 bool
229 BDebugLooper::RemoveTeamDebugger(BTeamDebugger* debugger)
231 if (debugger == NULL)
232 return false;
234 RemoveDebuggerJob job(debugger->Team());
235 return _DoJob(&job) == B_OK;
239 bool
240 BDebugLooper::RemoveTeamDebugger(team_id team)
242 if (team < 0)
243 return false;
245 RemoveDebuggerJob job(team);
246 return _DoJob(&job) == B_OK;
250 /*static*/ status_t
251 BDebugLooper::_MessageLoopEntry(void* data)
253 return ((BDebugLooper*)data)->_MessageLoop();
257 status_t
258 BDebugLooper::_MessageLoop()
260 while (true) {
261 // prepare the wait info array
262 int32 debuggerCount = fDebuggers.CountItems();
263 object_wait_info waitInfos[debuggerCount + 1];
265 for (int32 i = 0; i < debuggerCount; i++) {
266 waitInfos[i].object
267 = fDebuggers.ItemAt(i)->debugger->DebuggerPort();
268 waitInfos[i].type = B_OBJECT_TYPE_PORT;
269 waitInfos[i].events = B_EVENT_READ;
272 waitInfos[debuggerCount].object = fEventSemaphore;
273 waitInfos[debuggerCount].type = B_OBJECT_TYPE_SEMAPHORE;
274 waitInfos[debuggerCount].events = B_EVENT_ACQUIRE_SEMAPHORE;
276 // wait for the next event
277 wait_for_objects(waitInfos, debuggerCount + 1);
279 AutoLocker<BLocker> locker(fLock);
281 // handle all pending jobs
282 bool handledJobs = fJobs->Head() != NULL;
283 while (Job* job = fJobs->RemoveHead())
284 job->Done(job->Do(this));
286 // acquire notification semaphore and mark unnotified
287 if ((waitInfos[debuggerCount].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0)
288 acquire_sem(fEventSemaphore);
289 fNotified = false;
291 if (fTerminating)
292 return B_OK;
294 // Always loop when jobs were executed, since that might add/remove
295 // debuggers.
296 if (handledJobs)
297 continue;
299 // read a pending port message
300 for (int32 i = 0; i < debuggerCount; i++) {
301 if ((waitInfos[i].events & B_EVENT_READ) != 0) {
302 Debugger* debugger = fDebuggers.ItemAt(i);
304 // read the message
305 debug_debugger_message_data message;
306 int32 code;
307 ssize_t messageSize = read_port(
308 debugger->debugger->DebuggerPort(), &code, &message,
309 sizeof(message));
310 if (messageSize < 0)
311 continue;
313 // handle the message
314 bool continueThread = debugger->handler->HandleDebugMessage(
315 code, message);
317 // If requested, tell the thread to continue (only when there
318 // is a thread and the message was synchronous).
319 if (continueThread && message.origin.thread >= 0
320 && message.origin.nub_port >= 0) {
321 debugger->debugger->ContinueThread(message.origin.thread);
324 // Handle only one message -- the hook might have added/removed
325 // debuggers which makes further iteration problematic.
326 break;
333 status_t
334 BDebugLooper::_DoJob(Job* job)
336 AutoLocker<BLocker> locker(fLock);
338 // execute directly, if in looper thread or not running yet
339 if (fThread < 0 || fThread == find_thread(NULL))
340 return job->Do(this);
342 // execute in the looper thread
343 fJobs->Add(job);
344 _Notify();
346 return job->Wait(fLock);
350 void
351 BDebugLooper::_Notify()
353 if (fNotified)
354 return;
356 fNotified = true;
357 release_sem(fEventSemaphore);