Merge pull request #571 from jyrkive/fix-crash-on-openmp-relaunch

Fix some startup crashes
This commit is contained in:
Ignacio R. Morelle 2016-01-25 00:46:19 -03:00
commit a34d036279
4 changed files with 90 additions and 26 deletions

View file

@ -1211,6 +1211,9 @@
[entry]
name = "Justin DiSabatino (Turuk)"
[/entry]
[entry]
name = "Jyrki Vesterinen"
[/entry]
[entry]
name = "Kamil Kaczmarczyk (lampak)"
[/entry]

View file

@ -675,7 +675,15 @@ std::string get_cwd()
std::string get_exe_dir()
{
#ifdef _WIN32
return get_cwd();
wchar_t process_path[MAX_PATH];
SetLastError(ERROR_SUCCESS);
GetModuleFileNameW(NULL, process_path, MAX_PATH);
if (GetLastError() != ERROR_SUCCESS) {
return get_cwd();
}
path exe(process_path);
return exe.parent_path().string();
#else
if (bfs::exists("/proc/")) {
path self_exe("/proc/self/exe");

View file

@ -240,6 +240,12 @@ std::string &strip(std::string &str);
/** Remove whitespace from the back of the string 'str'. */
std::string &strip_end(std::string &str);
/** Surround the string 'str' with double quotes. */
inline std::string quote(const std::string &str)
{
return '"' + str + '"';
}
/** Convert no, false, off, 0, 0.0 to false, empty to def, and others to true */
bool string_bool(const std::string& str,bool def=false);

View file

@ -49,6 +49,7 @@
#include "serialization/binary_or_text.hpp" // for config_writer
#include "serialization/parser.hpp" // for read
#include "serialization/preprocessor.hpp" // for preproc_define, etc
#include "serialization/string_utils.hpp"
#include "serialization/validator.hpp" // for strict_validation_enabled
#include "serialization/unicode_cast.hpp"
#include "sound.hpp" // for commit_music_changes, etc
@ -71,6 +72,7 @@
#endif // _MSC_VER
#include <SDL.h> // for SDL_Init, SDL_INIT_TIMER
#include <boost/bind.hpp>
#include <boost/foreach.hpp> // for auto_any_base, etc
#include <boost/iostreams/categories.hpp> // for input, output
#include <boost/iostreams/copy.hpp> // for copy
@ -82,6 +84,7 @@
#include <boost/scoped_ptr.hpp> // for scoped_ptr
#include <boost/tuple/tuple.hpp> // for tuple
#include <algorithm> // for transform
#include <cerrno> // for ENOMEM
#include <clocale> // for setlocale, NULL, LC_ALL, etc
#include <cstdio> // for remove, fprintf, stderr
@ -90,6 +93,7 @@
#include <exception> // for exception
#include <fstream> // for operator<<, basic_ostream, etc
#include <iostream> // for cerr, cout
#include <vector>
//#define NO_CATCH_AT_GAME_END
@ -922,6 +926,40 @@ static void wesnoth_terminate_handler(int) {
}
#endif
#if defined(_OPENMP) && _MSC_VER >= 1600
static void restart_process(const std::vector<std::string>& commandline)
{
wchar_t process_path[MAX_PATH];
SetLastError(ERROR_SUCCESS);
GetModuleFileNameW(NULL, process_path, MAX_PATH);
if (GetLastError() != ERROR_SUCCESS)
{
throw std::runtime_error("Failed to retrieve the process path");
}
std::wstring commandline_str = unicode_cast<std::wstring>(utils::join(commandline, " "));
// CreateProcessW is allowed to modify the passed command line.
// Therefore we need to copy it.
wchar_t* commandline_c_str = new wchar_t[commandline_str.length() + 1];
commandline_str.copy(commandline_c_str, commandline_str.length());
commandline_c_str[commandline_str.length()] = L'\0';
STARTUPINFOW startup_info;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
PROCESS_INFORMATION process_info;
ZeroMemory(&process_info, sizeof(process_info));
CreateProcessW(process_path, commandline_c_str, NULL, NULL,
false, 0u, NULL, NULL, &startup_info, &process_info);
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
std::exit(EXIT_SUCCESS);
}
#endif
#if defined(__native_client__) || (defined(__APPLE__) && SDL_VERSION_ATLEAST(2, 0, 0))
extern "C" int wesnoth_main(int argc, char** argv);
int wesnoth_main(int argc, char** argv)
@ -934,31 +972,6 @@ int main(int argc, char** argv)
VLDEnable();
#endif
#ifdef _OPENMP
// Wesnoth is a special case for OMP
// OMP wait strategy is to have threads busy-loop for 100ms
// if there is nothing to do, they then go to sleep.
// this avoids the scheduler putting the thread to sleep when work
// is about to be available
//
// However Wesnoth has a lot of very small jobs that need to be done
// at each redraw => 50fps every 2ms.
// All the threads are thus busy-waiting all the time, hogging the CPU
// To avoid that problem, we need to set the OMP_WAIT_POLICY env var
// but that var is read by OMP at library loading time (before main)
// thus the relaunching of ourselves after setting the variable.
#if !defined(_WIN32) && !defined(__APPLE__)
if (!getenv("OMP_WAIT_POLICY")) {
setenv("OMP_WAIT_POLICY", "PASSIVE", 1);
execv(argv[0], argv);
}
#elif _MSC_VER >= 1600
if (!getenv("OMP_WAIT_POLICY")) {
_putenv_s("OMP_WAIT_POLICY", "PASSIVE");
_execv(argv[0], argv);
}
#endif
#endif //_OPENMP
#ifdef _WIN32
(void)argc;
(void)argv;
@ -986,6 +999,34 @@ int main(int argc, char** argv)
}
#endif
assert(!args.empty());
#ifdef _OPENMP
// Wesnoth is a special case for OMP
// OMP wait strategy is to have threads busy-loop for 100ms
// if there is nothing to do, they then go to sleep.
// this avoids the scheduler putting the thread to sleep when work
// is about to be available
//
// However Wesnoth has a lot of very small jobs that need to be done
// at each redraw => 50fps every 2ms.
// All the threads are thus busy-waiting all the time, hogging the CPU
// To avoid that problem, we need to set the OMP_WAIT_POLICY env var
// but that var is read by OMP at library loading time (before main)
// thus the relaunching of ourselves after setting the variable.
#if !defined(_WIN32) && !defined(__APPLE__)
if (!getenv("OMP_WAIT_POLICY")) {
setenv("OMP_WAIT_POLICY", "PASSIVE", 1);
execv(argv[0], argv);
}
#elif _MSC_VER >= 1600
if (!getenv("OMP_WAIT_POLICY")) {
_putenv_s("OMP_WAIT_POLICY", "PASSIVE");
args[0] = utils::quote(args[0]);
restart_process(args);
}
#endif
#endif //_OPENMP
if(SDL_Init(SDL_INIT_TIMER) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
return(1);
@ -1021,6 +1062,12 @@ int main(int argc, char** argv)
else if(filesystem::file_exists(exe_dir + "/../data/_main.cfg")) {
auto_dir = filesystem::normalize_path(exe_dir + "/..");
}
// In Windows debug builds, the EXE is placed away from the game data dir
// (in projectfiles\VCx\Debug), but the working directory is set to the
// game data dir. Thus, check if the working dir is the game data dir.
else if(filesystem::file_exists(filesystem::get_cwd() + "/data/_main.cfg")) {
auto_dir = filesystem::get_cwd();
}
if(!auto_dir.empty()) {
std::cerr << "Automatically found a possible data directory at "