diff --git a/headers/private/app/RegistrarDefs.h b/headers/private/app/RegistrarDefs.h index d54581f402..2afb90adcb 100644 --- a/headers/private/app/RegistrarDefs.h +++ b/headers/private/app/RegistrarDefs.h @@ -104,6 +104,9 @@ enum { B_REG_UPDATE_DISK_DEVICE = 'rgud', B_REG_DEVICE_START_WATCHING = 'rgwd', B_REG_DEVICE_STOP_WATCHING = 'rgsd', + + // debug_server notifications + B_REG_TEAM_DEBUGGER_ALERT = 'rtda', }; // B_REG_MIME_SET_PARAM "which" constants diff --git a/src/servers/debug/DebugServer.cpp b/src/servers/debug/DebugServer.cpp index 75ced93131..8f25137605 100644 --- a/src/servers/debug/DebugServer.cpp +++ b/src/servers/debug/DebugServer.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -115,6 +116,7 @@ private: const void *address, char *buffer, int32 bufferSize); void _PrintStackTrace(thread_id thread); void _NotifyAppServer(team_id team); + void _NotifyRegistrar(team_id team, bool openAlert, bool stopShutdown); status_t _InitGUI(); @@ -258,8 +260,7 @@ TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL; // DebugServer -class DebugServer : public BServer -{ +class DebugServer : public BServer { public: DebugServer(status_t &error); @@ -558,16 +559,20 @@ TeamDebugHandler::_HandleMessage(DebugMessage *message) } else if (USE_GUI && _AreGUIServersAlive() && _InitGUI() == B_OK) { // normal app -- tell the user _NotifyAppServer(fTeam); + _NotifyRegistrar(fTeam, true, false); char buffer[1024]; snprintf(buffer, sizeof(buffer), "The application:\n\n %s\n\n" "has encountered an error which prevents it from continuing. Haiku " "will terminate the application and clean up.", fTeamInfo.args); + // TODO: It would be nice if the alert would go away automatically + // if someone else kills our teams. BAlert *alert = new BAlert(NULL, buffer, "Debug", "OK", NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); int32 result = alert->Go(); kill = (result == 1); + _NotifyRegistrar(fTeam, false, !kill); } return kill; @@ -688,6 +693,22 @@ TeamDebugHandler::_NotifyAppServer(team_id team) roster.ApplicationCrashed(team); } + +void +TeamDebugHandler::_NotifyRegistrar(team_id team, bool openAlert, + bool stopShutdown) +{ + BMessage notify(BPrivate::B_REG_TEAM_DEBUGGER_ALERT); + notify.AddInt32("team", team); + notify.AddBool("open", openAlert); + notify.AddBool("stop shutdown", stopShutdown); + + BRoster::Private roster; + BMessage reply; + roster.SendTo(¬ify, &reply, false); +} + + // _InitGUI status_t TeamDebugHandler::_InitGUI() @@ -895,7 +916,7 @@ DebugServer::Init() return B_OK; } -// QuitRequested + bool DebugServer::QuitRequested() { diff --git a/src/servers/registrar/Registrar.cpp b/src/servers/registrar/Registrar.cpp index c3c8a460f3..4c39d7f2d0 100644 --- a/src/servers/registrar/Registrar.cpp +++ b/src/servers/registrar/Registrar.cpp @@ -222,6 +222,7 @@ Registrar::_MessageReceived(BMessage *message) break; } + // shutdown process case B_REG_SHUT_DOWN: { PRINT(("B_REG_SHUT_DOWN\n")); @@ -229,6 +230,12 @@ Registrar::_MessageReceived(BMessage *message) _HandleShutDown(message); break; } + case B_REG_TEAM_DEBUGGER_ALERT: + { + if (fShutdownProcess != NULL) + fShutdownProcess->PostMessage(message); + break; + } // roster requests case B_REG_ADD_APP: diff --git a/src/servers/registrar/ShutdownProcess.cpp b/src/servers/registrar/ShutdownProcess.cpp index a0ae031c77..169278d0ec 100644 --- a/src/servers/registrar/ShutdownProcess.cpp +++ b/src/servers/registrar/ShutdownProcess.cpp @@ -62,13 +62,13 @@ static const bigtime_t kNonAppQuitTimeout = 500000; // 0.5 s static const bigtime_t kDisplayAbortingAppTimeout = 3000000; // 3 s -// message what fields +// message what fields (must not clobber the registrar's message namespace) enum { MSG_PHASE_TIMED_OUT = 'phto', MSG_DONE = 'done', MSG_KILL_APPLICATION = 'kill', MSG_CANCEL_SHUTDOWN = 'cncl', - MSG_REBOOT_SYSTEM = 'rbot', + MSG_REBOOT_SYSTEM = 'lbot', }; // internal events @@ -79,6 +79,7 @@ enum { APP_QUIT_EVENT, KILL_APP_EVENT, REBOOT_SYSTEM_EVENT, + DEBUG_EVENT }; // phases @@ -790,21 +791,15 @@ PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team)); if (message->FindInt32("team", &team) != B_OK) break; - BAutolock _(fWorkerLock); - // post the event _PushEvent(KILL_APP_EVENT, team, fCurrentPhase); - break; } case MSG_CANCEL_SHUTDOWN: { - BAutolock _(fWorkerLock); - // post the event _PushEvent(ABORT_EVENT, -1, fCurrentPhase); - break; } @@ -812,7 +807,6 @@ PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team)); { // post the event _PushEvent(REBOOT_SYSTEM_EVENT, -1, INVALID_PHASE); - break; } @@ -820,7 +814,34 @@ PRINT(("MSG_PHASE_TIMED_OUT: phase: %ld, team: %ld\n", phase, team)); { // notify the registrar that we're done be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app); + break; + } + case B_REG_TEAM_DEBUGGER_ALERT: + { + bool stopShutdown; + if (message->FindBool("stop shutdown", &stopShutdown) == B_OK + && stopShutdown) { + // post abort event to the worker + _PushEvent(ABORT_EVENT, -1, fCurrentPhase); + break; + } + + bool open; + team_id team; + if (message->FindInt32("team", &team) != B_OK + || message->FindBool("open", &open) != B_OK) + break; + + BAutolock _(fWorkerLock); + if (open) { + PRINT(("B_REG_TEAM_DEBUGGER_ALERT: insert %ld\n", team)); + fDebuggedTeams.insert(team); + } else { + PRINT(("B_REG_TEAM_DEBUGGER_ALERT: remove %ld\n", team)); + fDebuggedTeams.erase(team); + _PushEvent(DEBUG_EVENT, -1, fCurrentPhase); + } break; } @@ -1145,10 +1166,9 @@ ShutdownProcess::_GetNextEvent(uint32 &eventType, thread_id &team, int32 &phase, do { error = acquire_sem(fInternalEventSemaphore); } while (error == B_INTERRUPTED); - + if (error != B_OK) return error; - } else { status_t error = acquire_sem_etc(fInternalEventSemaphore, 1, B_RELATIVE_TIMEOUT, 0); @@ -1157,10 +1177,10 @@ ShutdownProcess::_GetNextEvent(uint32 &eventType, thread_id &team, int32 &phase, return B_OK; } } - + // get the event BAutolock _(fWorkerLock); - + InternalEvent *event = fInternalEvents->Head(); fInternalEvents->Remove(event); @@ -1259,14 +1279,17 @@ ShutdownProcess::_WorkerDoShutdown() // phase 1: terminate the user apps _SetPhase(USER_APP_TERMINATION_PHASE); _QuitApps(fUserApps, false); + _WaitForDebuggedTeams(); // phase 2: terminate the system apps _SetPhase(SYSTEM_APP_TERMINATION_PHASE); _QuitApps(fSystemApps, true); + _WaitForDebuggedTeams(); // phase 3: terminate the background apps _SetPhase(BACKGROUND_APP_TERMINATION_PHASE); _QuitBackgroundApps(); + _WaitForDebuggedTeams(); // phase 4: terminate the other processes _SetPhase(OTHER_PROCESSES_TERMINATION_PHASE); @@ -1274,6 +1297,7 @@ ShutdownProcess::_WorkerDoShutdown() _ScheduleTimeoutEvent(kBackgroundAppQuitTimeout, -1); _WaitForBackgroundApps(); _KillBackgroundApps(); + _WaitForDebuggedTeams(); // we're through: do the shutdown _SetPhase(DONE_PHASE); @@ -1311,7 +1335,49 @@ ShutdownProcess::_WorkerDoShutdown() #endif } -// _QuitApps + +bool +ShutdownProcess::_WaitForApp(team_id team, AppInfoList *list, bool systemApps) +{ + uint32 event; + do { + team_id eventTeam; + int32 phase; + status_t error = _GetNextEvent(event, eventTeam, phase, true); + if (error != B_OK) + throw_error(error); + + if (event == APP_QUIT_EVENT && eventTeam == team) + return true; + + if (event == TIMEOUT_EVENT && eventTeam == team) + return false; + + if (event == ABORT_EVENT) { + if (systemApps) { + // If the app requests aborting the shutdown, we don't need + // to wait any longer. It has processed the request and + // won't quit by itself. We ignore this for system apps. + if (eventTeam == team) + return false; + } else { + PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled " + "by team %ld (-1 => user)\n", eventTeam)); + + _DisplayAbortingApp(team); + throw_error(B_SHUTDOWN_CANCELLED); + } + } + + BAutolock _(fWorkerLock); + if (list != NULL && !list->InfoFor(team)) + return true; + } while (event != NO_EVENT); + + return false; +} + + void ShutdownProcess::_QuitApps(AppInfoList &list, bool systemApps) { @@ -1401,44 +1467,7 @@ ShutdownProcess::_QuitApps(AppInfoList &list, bool systemApps) _ScheduleTimeoutEvent(kAppQuitTimeout, team); // wait for the app to die or for the timeout to occur - bool appGone = false; - do { - team_id eventTeam; - int32 phase; - status_t error = _GetNextEvent(event, eventTeam, phase, true); - if (error != B_OK) - throw_error(error); - - if ((event == APP_QUIT_EVENT) - && eventTeam == team) { - appGone = true; - } - - if (event == TIMEOUT_EVENT && eventTeam == team) - break; - - if (event == ABORT_EVENT) { - if (systemApps) { - // If the app requests aborting the shutdown, we don't need - // to wait any longer. It has processed the request and - // won't quit by itself. We ignore this for system apps. - if (eventTeam == team) - break; - } else { - PRINT(("ShutdownProcess::_QuitApps(): shutdown cancelled " - "by team %ld (-1 => user)\n", eventTeam)); - - _DisplayAbortingApp(team); - throw_error(B_SHUTDOWN_CANCELLED); - } - } - - BAutolock _(fWorkerLock); - if (!list.InfoFor(team)) - break; - - } while (event != NO_EVENT); - + bool appGone = _WaitForApp(team, &list, systemApps); if (appGone) { // fine: the app finished in an orderly manner } else { @@ -1623,7 +1652,17 @@ void ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team, const char *appName, bool cancelAllowed) { - if (BPrivate::is_app_showing_modal_window(team)) { + bool debugged = false; + bool modal = false; + { + BAutolock _(fWorkerLock); + if (fDebuggedTeams.find(team) != fDebuggedTeams.end()) + debugged = true; + } + if (!debugged) + modal = BPrivate::is_app_showing_modal_window(team); + + if (modal) { // app blocks on a modal window char buffer[1024]; snprintf(buffer, sizeof(buffer), "The application \"%s\" might be " @@ -1631,7 +1670,9 @@ ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team, _SetShutdownWindowText(buffer); _SetShutdownWindowCurrentApp(team); _SetShutdownWindowKillButtonEnabled(true); + } + if (modal || debugged) { // wait for something to happen bool appGone = false; while (true) { @@ -1651,7 +1692,7 @@ ShutdownProcess::_QuitBlockingApp(AppInfoList &list, team_id team, break; if (event == ABORT_EVENT) { - if (cancelAllowed) { + if (cancelAllowed || debugged) { PRINT(("ShutdownProcess::_QuitBlockingApp(): shutdown " "cancelled by team %ld (-1 => user)\n", eventTeam)); @@ -1758,3 +1799,39 @@ ShutdownProcess::_DisplayAbortingApp(team_id team) } } + +/*! Waits until the debugged team list is empty, ie. when there is no one + left to debug. +*/ +void +ShutdownProcess::_WaitForDebuggedTeams() +{ + PRINT(("ShutdownProcess::_WaitForDebuggedTeams()\n")); + { + BAutolock _(fWorkerLock); + if (fDebuggedTeams.empty()) + return; + } + + PRINT((" not empty!\n")); + + // wait for something to happen + while (true) { + uint32 event; + team_id eventTeam; + int32 phase; + status_t error = _GetNextEvent(event, eventTeam, phase, true); + if (error != B_OK) + throw_error(error); + + if (event == ABORT_EVENT) + throw_error(B_SHUTDOWN_CANCELLED); + + BAutolock _(fWorkerLock); + if (fDebuggedTeams.empty()) { + PRINT((" out empty")); + return; + } + } +} + diff --git a/src/servers/registrar/ShutdownProcess.h b/src/servers/registrar/ShutdownProcess.h index 8e7eee5461..c1f8ac1e53 100644 --- a/src/servers/registrar/ShutdownProcess.h +++ b/src/servers/registrar/ShutdownProcess.h @@ -70,6 +70,7 @@ private: status_t _Worker(); void _WorkerDoShutdown(); + bool _WaitForApp(team_id team, AppInfoList *list, bool systemApps); void _QuitApps(AppInfoList &list, bool systemApps); void _QuitBackgroundApps(); void _WaitForBackgroundApps(); @@ -78,6 +79,7 @@ private: void _QuitBlockingApp(AppInfoList &list, team_id team, const char *appName, bool cancelAllowed); void _DisplayAbortingApp(team_id team); + void _WaitForDebuggedTeams(); private: class TimeoutEvent; @@ -97,6 +99,7 @@ private: AppInfoList fSystemApps; AppInfoList fUserApps; AppInfoList fBackgroundApps; + hash_set fDebuggedTeams; TimeoutEvent *fTimeoutEvent; InternalEventList *fInternalEvents; sem_id fInternalEventSemaphore;