1 /* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <DebugLooper.h> 8 9 #include <new> 10 11 #include <AutoLocker.h> 12 #include <DebugMessageHandler.h> 13 #include <TeamDebugger.h> 14 #include <util/DoublyLinkedList.h> 15 16 17 struct BDebugLooper::Debugger { 18 BTeamDebugger* debugger; 19 BDebugMessageHandler* handler; 20 21 Debugger(BTeamDebugger* debugger, BDebugMessageHandler* handler) 22 : 23 debugger(debugger), 24 handler(handler) 25 { 26 } 27 }; 28 29 30 struct BDebugLooper::Job : DoublyLinkedListLinkImpl<Job> { 31 Job() 32 : 33 fDoneSemaphore(-1) 34 { 35 } 36 37 virtual ~Job() 38 { 39 } 40 41 status_t Wait(BLocker& lock) 42 { 43 fDoneSemaphore = create_sem(0, "debug looper job"); 44 45 lock.Unlock(); 46 47 while (acquire_sem(fDoneSemaphore) == B_INTERRUPTED) { 48 } 49 50 lock.Lock(); 51 52 delete_sem(fDoneSemaphore); 53 fDoneSemaphore = -1; 54 55 return fResult; 56 } 57 58 void Done(status_t result) 59 { 60 fResult = result; 61 release_sem(fDoneSemaphore); 62 } 63 64 virtual status_t Do(BDebugLooper* looper) = 0; 65 66 protected: 67 sem_id fDoneSemaphore; 68 status_t fResult; 69 }; 70 71 72 struct BDebugLooper::JobList : DoublyLinkedList<Job> { 73 }; 74 75 76 struct BDebugLooper::AddDebuggerJob : Job { 77 AddDebuggerJob(BTeamDebugger* debugger, 78 BDebugMessageHandler* handler) 79 : 80 fDebugger(debugger), 81 fHandler(handler) 82 { 83 } 84 85 virtual status_t Do(BDebugLooper* looper) 86 { 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; 91 } 92 93 return B_OK; 94 } 95 96 private: 97 BTeamDebugger* fDebugger; 98 BDebugMessageHandler* fHandler; 99 }; 100 101 102 struct BDebugLooper::RemoveDebuggerJob : Job { 103 RemoveDebuggerJob(team_id team) 104 : 105 fTeam(team) 106 { 107 } 108 109 virtual status_t Do(BDebugLooper* looper) 110 { 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; 116 } 117 } 118 119 return B_ENTRY_NOT_FOUND; 120 } 121 122 private: 123 team_id fTeam; 124 }; 125 126 127 // #pragma mark - 128 129 130 BDebugLooper::BDebugLooper() 131 : 132 fLock("debug looper"), 133 fThread(-1), 134 fOwnsThread(false), 135 fTerminating(false), 136 fNotified(false), 137 fJobs(NULL), 138 fEventSemaphore(-1) 139 { 140 } 141 142 143 BDebugLooper::~BDebugLooper() 144 { 145 } 146 147 148 status_t 149 BDebugLooper::Init() 150 { 151 status_t error = fLock.InitCheck(); 152 if (error != B_OK) 153 return error; 154 155 AutoLocker<BLocker> locker(fLock); 156 157 if (fThread >= 0) 158 return B_BAD_VALUE; 159 160 if (fJobs == NULL) { 161 fJobs = new(std::nothrow) JobList; 162 if (fJobs == NULL) 163 return B_NO_MEMORY; 164 } 165 166 if (fEventSemaphore < 0) { 167 fEventSemaphore = create_sem(0, "debug looper event"); 168 if (fEventSemaphore < 0) 169 return fEventSemaphore; 170 } 171 172 return B_OK; 173 } 174 175 176 thread_id 177 BDebugLooper::Run(bool spawnThread) 178 { 179 AutoLocker<BLocker> locker(fLock); 180 181 if (fThread >= 0) 182 return B_BAD_VALUE; 183 184 fNotified = false; 185 186 if (spawnThread) { 187 fThread = spawn_thread(&_MessageLoopEntry, "debug looper", 188 B_NORMAL_PRIORITY, this); 189 if (fThread < 0) 190 return fThread; 191 192 fOwnsThread = true; 193 194 resume_thread(fThread); 195 return B_OK; 196 } 197 198 fThread = find_thread(NULL); 199 fOwnsThread = false; 200 201 _MessageLoop(); 202 return B_OK; 203 } 204 205 206 void 207 BDebugLooper::Quit() 208 { 209 AutoLocker<BLocker> locker(fLock); 210 211 fTerminating = true; 212 _Notify(); 213 } 214 215 216 status_t 217 BDebugLooper::AddTeamDebugger(BTeamDebugger* debugger, 218 BDebugMessageHandler* handler) 219 { 220 if (debugger == NULL || handler == NULL) 221 return B_BAD_VALUE; 222 223 AddDebuggerJob job(debugger, handler); 224 return _DoJob(&job); 225 } 226 227 228 bool 229 BDebugLooper::RemoveTeamDebugger(BTeamDebugger* debugger) 230 { 231 if (debugger == NULL) 232 return false; 233 234 RemoveDebuggerJob job(debugger->Team()); 235 return _DoJob(&job) == B_OK; 236 } 237 238 239 bool 240 BDebugLooper::RemoveTeamDebugger(team_id team) 241 { 242 if (team < 0) 243 return false; 244 245 RemoveDebuggerJob job(team); 246 return _DoJob(&job) == B_OK; 247 } 248 249 250 /*static*/ status_t 251 BDebugLooper::_MessageLoopEntry(void* data) 252 { 253 return ((BDebugLooper*)data)->_MessageLoop(); 254 } 255 256 257 status_t 258 BDebugLooper::_MessageLoop() 259 { 260 while (true) { 261 // prepare the wait info array 262 int32 debuggerCount = fDebuggers.CountItems(); 263 object_wait_info waitInfos[debuggerCount + 1]; 264 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; 270 } 271 272 waitInfos[debuggerCount].object = fEventSemaphore; 273 waitInfos[debuggerCount].type = B_OBJECT_TYPE_SEMAPHORE; 274 waitInfos[debuggerCount].events = B_EVENT_ACQUIRE_SEMAPHORE; 275 276 // wait for the next event 277 wait_for_objects(waitInfos, debuggerCount + 1); 278 279 AutoLocker<BLocker> locker(fLock); 280 281 // handle all pending jobs 282 bool handledJobs = fJobs->Head() != NULL; 283 while (Job* job = fJobs->RemoveHead()) 284 job->Done(job->Do(this)); 285 286 // acquire notification semaphore and mark unnotified 287 if ((waitInfos[debuggerCount].events & B_EVENT_ACQUIRE_SEMAPHORE) != 0) 288 acquire_sem(fEventSemaphore); 289 fNotified = false; 290 291 if (fTerminating) 292 return B_OK; 293 294 // Always loop when jobs were executed, since that might add/remove 295 // debuggers. 296 if (handledJobs) 297 continue; 298 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); 303 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; 312 313 // handle the message 314 bool continueThread = debugger->handler->HandleDebugMessage( 315 code, message); 316 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); 322 } 323 324 // Handle only one message -- the hook might have added/removed 325 // debuggers which makes further iteration problematic. 326 break; 327 } 328 } 329 } 330 } 331 332 333 status_t 334 BDebugLooper::_DoJob(Job* job) 335 { 336 AutoLocker<BLocker> locker(fLock); 337 338 // execute directly, if in looper thread or not running yet 339 if (fThread < 0 || fThread == find_thread(NULL)) 340 return job->Do(this); 341 342 // execute in the looper thread 343 fJobs->Add(job); 344 _Notify(); 345 346 return job->Wait(fLock); 347 } 348 349 350 void 351 BDebugLooper::_Notify() 352 { 353 if (fNotified) 354 return; 355 356 fNotified = true; 357 release_sem(fEventSemaphore); 358 } 359