diff --git a/source/lib/sysdep/os/win/wmi.cpp b/source/lib/sysdep/os/win/wmi.cpp index 0f325ac721..d60b6e7e78 100644 --- a/source/lib/sysdep/os/win/wmi.cpp +++ b/source/lib/sysdep/os/win/wmi.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2011 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -42,6 +42,8 @@ _COM_SMARTPTR_TYPEDEF(IEnumWbemClassObject, __uuidof(IEnumWbemClassObject)); static ModuleInitState initState; +static bool didInitCOM = false; + static Status Init() { HRESULT hr; @@ -49,6 +51,12 @@ static Status Init() hr = CoInitialize(0); ENSURE(hr == S_OK || hr == S_FALSE); // S_FALSE => already initialized + // balance calls to CoInitialize and CoUninitialize + if (hr == S_FALSE) + CoUninitialize(); + else if (hr == S_OK) + didInitCOM = true; + hr = CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, 0, EOAC_NONE, 0); if(FAILED(hr)) WARN_RETURN(ERR::_2); @@ -75,8 +83,18 @@ static void Shutdown() { pSvc->Release(); - // note: don't shut down COM because other modules may still be using it. - //CoUninitialize(); + if (didInitCOM) + { + /* From MSDN documentation: A thread must call CoUninitialize once for each successful call + * it has made to the CoInitialize or CoInitializeEx function, including any call that returns + * S_FALSE. Only the CoUninitialize call corresponding to the CoInitialize or CoInitializeEx + * call that initialized the library can close it. + * + * So it should be perfectly safe to call this, since it balances out the CoInitialize in Init + */ + CoUninitialize(); + didInitCOM = false; + } } void wmi_Shutdown() diff --git a/source/lib/sysdep/os/win/wsysdep.cpp b/source/lib/sysdep/os/win/wsysdep.cpp index f3540bb5c1..17a283f9e1 100644 --- a/source/lib/sysdep/os/win/wsysdep.cpp +++ b/source/lib/sysdep/os/win/wsysdep.cpp @@ -459,6 +459,9 @@ Status sys_pick_directory(OsPath& path) return INFO::OK; } + // Balance call to CoInitialize, which must have been successful + CoUninitialize(); + WARN_RETURN(StatusFromWin()); } diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 6068ce1229..82e028d986 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -102,6 +102,10 @@ #define MUST_INIT_X11 0 #endif +#if OS_WIN +extern void wmi_Shutdown(); +#endif + #include ERROR_GROUP(System); @@ -683,6 +687,12 @@ void Shutdown(int UNUSED(flags)) delete &g_Profiler; delete &g_ProfileViewer; TIMER_END(L"shutdown misc"); + +#if OS_WIN + TIMER_BEGIN(L"shutdown wmi"); + wmi_Shutdown(); + TIMER_END(L"shutdown wmi"); +#endif } #if OS_UNIX diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index aea7b78a5f..4bc3483e29 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -73,21 +73,6 @@ static InputProcessor g_Input; static GameLoopState state; GameLoopState* g_GameLoop = &state; -static void* LaunchWindow(void* data) -{ - debug_SetThreadName("atlas_window"); - - const wchar_t* windowName = reinterpret_cast(data); - Atlas_StartWindow(windowName); - return NULL; -} - -// Work out which Atlas window to launch, given the command-line arguments -static const wchar_t* FindWindowName(const CmdLineArgs& UNUSED(args)) -{ - return L"ScenarioEditor"; - // (This is a bit pointless - there's no choice since we've deleted the ActorViewer) -} static ErrorReactionInternal AtlasDisplayError(const wchar_t* text, size_t flags) { @@ -114,47 +99,20 @@ static void RendererIncrementalLoad() while (more && timer_Time() - startTime < maxTime); } -bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) +static void* RunEngine(void *data) { - // Load required symbols from the DLL - try - { - dll.LoadSymbol("Atlas_StartWindow", Atlas_StartWindow); - dll.LoadSymbol("Atlas_SetMessagePasser", Atlas_SetMessagePasser); - dll.LoadSymbol("Atlas_SetDataDirectory", Atlas_SetDataDirectory); - dll.LoadSymbol("Atlas_GLSetCurrent", Atlas_GLSetCurrent); - dll.LoadSymbol("Atlas_GLSwapBuffers", Atlas_GLSwapBuffers); - dll.LoadSymbol("Atlas_NotifyEndOfFrame", Atlas_NotifyEndOfFrame); - dll.LoadSymbol("Atlas_DisplayError", Atlas_DisplayError); - dll.LoadSymbol("Atlas_ReportError", Atlas_ReportError); - dll.LoadSymbol("ShareableMalloc", ShareableMallocFptr); - dll.LoadSymbol("ShareableFree", ShareableFreeFptr); - } - catch (PSERROR_DllLoader&) - { - debug_warn(L"Failed to initialise DLL"); - return false; - } + debug_SetThreadName("engine_thread"); - // Construct a message passer for communicating with Atlas - MessagePasserImpl msgPasser; - AtlasMessage::g_MessagePasser = &msgPasser; + // Set new main thread so that all the thread-safety checks pass + ThreadUtil::SetMainThread(); - // Pass our message handler to Atlas - Atlas_SetMessagePasser(&msgPasser); + const CmdLineArgs args = *reinterpret_cast(data); - // Tell Atlas the location of the data directory - const Paths paths(args); - Atlas_SetDataDirectory(paths.RData().string().c_str()); + MessagePasserImpl* msgPasser = (MessagePasserImpl*)AtlasMessage::g_MessagePasser; // Register all the handlers for message which might be passed back RegisterHandlers(); - - // Create a new thread, and launch the Atlas window inside that thread - const wchar_t* windowName = FindWindowName(args); - pthread_t uiThread; - pthread_create(&uiThread, NULL, LaunchWindow, reinterpret_cast(const_cast(windowName))); - + // Override ah_display_error to pass all errors to the Atlas UI AppHooks hooks = {0}; hooks.display_error = AtlasDisplayError; @@ -199,7 +157,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) { IMessage* msg; - while ((msg = msgPasser.Retrieve()) != NULL) + while ((msg = msgPasser->Retrieve()) != NULL) { recent_activity = true; @@ -294,7 +252,7 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) // (TODO: This should probably be done with something like semaphores) Atlas_NotifyEndOfFrame(); // (TODO: rename to NotifyEndOfQuiteShortProcessingPeriodSoPleaseSendMeNewMessages or something) SDL_Delay(50); - if (!msgPasser.IsEmpty()) + if (!msgPasser->IsEmpty()) break; time = timer_Time(); } @@ -306,10 +264,58 @@ bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) } } + return NULL; +} + +bool BeginAtlas(const CmdLineArgs& args, const DllLoader& dll) +{ + // Load required symbols from the DLL + try + { + dll.LoadSymbol("Atlas_StartWindow", Atlas_StartWindow); + dll.LoadSymbol("Atlas_SetMessagePasser", Atlas_SetMessagePasser); + dll.LoadSymbol("Atlas_SetDataDirectory", Atlas_SetDataDirectory); + dll.LoadSymbol("Atlas_GLSetCurrent", Atlas_GLSetCurrent); + dll.LoadSymbol("Atlas_GLSwapBuffers", Atlas_GLSwapBuffers); + dll.LoadSymbol("Atlas_NotifyEndOfFrame", Atlas_NotifyEndOfFrame); + dll.LoadSymbol("Atlas_DisplayError", Atlas_DisplayError); + dll.LoadSymbol("Atlas_ReportError", Atlas_ReportError); + dll.LoadSymbol("ShareableMalloc", ShareableMallocFptr); + dll.LoadSymbol("ShareableFree", ShareableFreeFptr); + } + catch (PSERROR_DllLoader&) + { + debug_warn(L"Failed to initialise DLL"); + return false; + } + + // Construct a message passer for communicating with Atlas + // (here so that it's scope lasts beyond the game thread) + MessagePasserImpl msgPasser; + AtlasMessage::g_MessagePasser = &msgPasser; + + // Pass our message handler to Atlas + Atlas_SetMessagePasser(&msgPasser); + + // Tell Atlas the location of the data directory + const Paths paths(args); + Atlas_SetDataDirectory(paths.RData().string().c_str()); + + // run the engine loop in a new thread + pthread_t engineThread; + pthread_create(&engineThread, NULL, RunEngine, reinterpret_cast(const_cast(&args))); + + // start Atlas UI on main thread + // (required for wxOSX/Cocoa compatbility as some parts of the API aren't thread-safe) + Atlas_StartWindow(L"ScenarioEditor"); + + // Wait for the engine to exit + pthread_join(engineThread, NULL); + // TODO: delete all remaining messages, to avoid memory leak warnings - // Wait for the UI to exit - pthread_join(uiThread, NULL); + // Restore main thread + ThreadUtil::SetMainThread(); // Clean up View::DestroyViews();