Merge branches 'crashlog_improvements', 'save_ext' into jgrpp
# Conflicts: # Makefile.src.in # projects/openttd_vs140.vcxproj # projects/openttd_vs140.vcxproj.filters # projects/openttd_vs141.vcxproj # projects/openttd_vs141.vcxproj.filters # projects/openttd_vs142.vcxproj # projects/openttd_vs142.vcxproj.filters # src/core/smallstack_type.hpp # src/linkgraph/linkgraphjob.cpp # src/linkgraph/linkgraphjob.h # src/misc.cpp # src/network/network_udp.cpp # src/openttd.cpp # src/saveload/saveload.cpppull/88/head
commit
361758b516
@ -0,0 +1,104 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file thread.h Base of all threads. */
|
||||||
|
|
||||||
|
#ifndef THREAD_H
|
||||||
|
#define THREAD_H
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include <system_error>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
/** Signal used for signalling we knowingly want to end the thread. */
|
||||||
|
class OTTDThreadExitSignal { };
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep on the current thread for a defined time.
|
||||||
|
* @param milliseconds Time to sleep for in milliseconds.
|
||||||
|
*/
|
||||||
|
inline void CSleep(int milliseconds)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name the thread this function is called on for the debugger.
|
||||||
|
* @param name Name to set for the thread..
|
||||||
|
*/
|
||||||
|
void SetCurrentThreadName(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the current thread, if any.
|
||||||
|
* @param str The start of the buffer.
|
||||||
|
* @param last The last char of the buffer.
|
||||||
|
* @return Number of chars written to str.
|
||||||
|
*/
|
||||||
|
int GetCurrentThreadName(char *str, const char *last);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current thread as the "main" thread
|
||||||
|
*/
|
||||||
|
void SetSelfAsMainThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the current thread definitely the "main" thread. If in doubt returns false.
|
||||||
|
*/
|
||||||
|
bool IsMainThread();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the current thread definitely a "non-main" thread. If in doubt returns false.
|
||||||
|
*/
|
||||||
|
bool IsNonMainThread();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new thread.
|
||||||
|
* @tparam TFn Type of the function to call on the thread.
|
||||||
|
* @tparam TArgs Type of the parameters of the thread function.
|
||||||
|
* @param thr Pointer to a thread object; may be \c NULL if a detached thread is wanted.
|
||||||
|
* @param name Name of the thread.
|
||||||
|
* @param _Fx Function to call on the thread.
|
||||||
|
* @param _Ax Arguments for the thread function.
|
||||||
|
* @return True if the thread was successfully started, false otherwise.
|
||||||
|
*/
|
||||||
|
template<class TFn, class... TArgs>
|
||||||
|
inline bool StartNewThread(std::thread *thr, const char *name, TFn&& _Fx, TArgs&&... _Ax)
|
||||||
|
{
|
||||||
|
#ifndef NO_THREADS
|
||||||
|
try {
|
||||||
|
std::thread t([] (const char *name, TFn&& F, TArgs&&... A) {
|
||||||
|
SetCurrentThreadName(name);
|
||||||
|
try {
|
||||||
|
/* Call user function with the given arguments. */
|
||||||
|
F(A...);
|
||||||
|
} catch (OTTDThreadExitSignal&) {
|
||||||
|
} catch (...) {
|
||||||
|
NOT_REACHED();
|
||||||
|
}
|
||||||
|
}, name, std::forward<TFn>(_Fx), std::forward<TArgs>(_Ax)...);
|
||||||
|
|
||||||
|
if (thr != NULL) {
|
||||||
|
*thr = std::move(t);
|
||||||
|
} else {
|
||||||
|
t.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (const std::system_error& e) {
|
||||||
|
/* Something went wrong, the system we are running on might not support threads. */
|
||||||
|
DEBUG(misc, 1, "Can't create thread '%s': %s", name, e.what());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* THREAD_H */
|
@ -1,149 +0,0 @@
|
|||||||
/* $Id$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of OpenTTD.
|
|
||||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
||||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file thread.h Base of all threads. */
|
|
||||||
|
|
||||||
#ifndef THREAD_H
|
|
||||||
#define THREAD_H
|
|
||||||
|
|
||||||
/** Definition of all thread entry functions. */
|
|
||||||
typedef void (*OTTDThreadFunc)(void *);
|
|
||||||
|
|
||||||
/** Signal used for signalling we knowingly want to end the thread. */
|
|
||||||
class OTTDThreadExitSignal { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Thread Object which works on all our supported OSes.
|
|
||||||
*/
|
|
||||||
class ThreadObject {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Virtual destructor to allow 'delete' operator to work properly.
|
|
||||||
*/
|
|
||||||
virtual ~ThreadObject() {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exit this thread.
|
|
||||||
*/
|
|
||||||
virtual bool Exit() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Join this thread.
|
|
||||||
*/
|
|
||||||
virtual void Join() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a thread; proc will be called as first function inside the thread,
|
|
||||||
* with optional params.
|
|
||||||
* @param proc The procedure to call inside the thread.
|
|
||||||
* @param param The params to give with 'proc'.
|
|
||||||
* @param thread Place to store a pointer to the thread in. May be NULL.
|
|
||||||
* @param name A name for the thread. May be NULL.
|
|
||||||
* @return True if the thread was started correctly.
|
|
||||||
*/
|
|
||||||
static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread = NULL, const char *name = NULL);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cross-platform Mutex
|
|
||||||
*/
|
|
||||||
class ThreadMutex {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a new mutex.
|
|
||||||
*/
|
|
||||||
static ThreadMutex *New();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Virtual Destructor to avoid compiler warnings.
|
|
||||||
*/
|
|
||||||
virtual ~ThreadMutex() {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Begin the critical section
|
|
||||||
* @param allow_recursive Whether recursive locking is intentional.
|
|
||||||
* If false, NOT_REACHED() will be called when the mutex is already locked
|
|
||||||
* by the current thread.
|
|
||||||
*/
|
|
||||||
virtual void BeginCritical(bool allow_recursive = false) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* End of the critical section
|
|
||||||
* @param allow_recursive Whether recursive unlocking is intentional.
|
|
||||||
* If false, NOT_REACHED() will be called when the mutex was locked more
|
|
||||||
* than once by the current thread.
|
|
||||||
*/
|
|
||||||
virtual void EndCritical(bool allow_recursive = false) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for a signal to be send.
|
|
||||||
* @pre You must be in the critical section.
|
|
||||||
* @note While waiting the critical section is left.
|
|
||||||
* @post You will be in the critical section.
|
|
||||||
*/
|
|
||||||
virtual void WaitForSignal() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a signal and wake the 'thread' that was waiting for it.
|
|
||||||
*/
|
|
||||||
virtual void SendSignal() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple mutex locker to keep a mutex locked until the locker goes out of scope.
|
|
||||||
*/
|
|
||||||
class ThreadMutexLocker {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Lock the mutex and keep it locked for the life time of this object.
|
|
||||||
* @param mutex Mutex to be locked.
|
|
||||||
*/
|
|
||||||
ThreadMutexLocker(ThreadMutex *mutex) : mutex(mutex) { mutex->BeginCritical(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlock the mutex.
|
|
||||||
*/
|
|
||||||
~ThreadMutexLocker() { this->mutex->EndCritical(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ThreadMutexLocker(const ThreadMutexLocker &) { NOT_REACHED(); }
|
|
||||||
ThreadMutexLocker &operator=(const ThreadMutexLocker &) { NOT_REACHED(); return *this; }
|
|
||||||
ThreadMutex *mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get number of processor cores in the system, including HyperThreading or similar.
|
|
||||||
* @return Total number of processor cores.
|
|
||||||
*/
|
|
||||||
uint GetCPUCoreCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current thread as the "main" thread
|
|
||||||
*/
|
|
||||||
void SetSelfAsMainThread();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the current thread definitely the "main" thread. If in doubt returns false.
|
|
||||||
*/
|
|
||||||
bool IsMainThread();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the current thread definitely a "non-main" thread. If in doubt returns false.
|
|
||||||
*/
|
|
||||||
bool IsNonMainThread();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the current thread, if any.
|
|
||||||
* @param str The start of the buffer.
|
|
||||||
* @param last The last char of the buffer.
|
|
||||||
* @return Number of chars written to str.
|
|
||||||
*/
|
|
||||||
int GetThreadName(char *str, const char *last);
|
|
||||||
|
|
||||||
#endif /* THREAD_H */
|
|
@ -1,42 +0,0 @@
|
|||||||
/* $Id$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of OpenTTD.
|
|
||||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
||||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file thread_none.cpp No-Threads-Available implementation of Threads */
|
|
||||||
|
|
||||||
#include "../stdafx.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
#include "../safeguards.h"
|
|
||||||
|
|
||||||
/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
|
|
||||||
{
|
|
||||||
if (thread != NULL) *thread = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Mutex that doesn't do locking because it ain't needed when there're no threads */
|
|
||||||
class ThreadMutex_None : public ThreadMutex {
|
|
||||||
public:
|
|
||||||
virtual void BeginCritical(bool allow_recursive = false) {}
|
|
||||||
virtual void EndCritical(bool allow_recursive = false) {}
|
|
||||||
virtual void WaitForSignal() {}
|
|
||||||
virtual void SendSignal() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ ThreadMutex *ThreadMutex::New()
|
|
||||||
{
|
|
||||||
return new ThreadMutex_None();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSelfAsMainThread() { }
|
|
||||||
|
|
||||||
bool IsMainThread() { return true; }
|
|
||||||
bool IsNonMainThread() { return false; }
|
|
||||||
|
|
||||||
int GetThreadName(char *str, const char *last) { return 0; }
|
|
@ -1,155 +0,0 @@
|
|||||||
/* $Id$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of OpenTTD.
|
|
||||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
||||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file thread_os2.cpp OS/2 implementation of Threads. */
|
|
||||||
|
|
||||||
#include "../stdafx.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
#define INCL_DOS
|
|
||||||
#include <os2.h>
|
|
||||||
#include <process.h>
|
|
||||||
|
|
||||||
#include "../safeguards.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OS/2 version for ThreadObject.
|
|
||||||
*/
|
|
||||||
class ThreadObject_OS2 : public ThreadObject {
|
|
||||||
private:
|
|
||||||
TID thread; ///< System thread identifier.
|
|
||||||
OTTDThreadFunc proc; ///< External thread procedure.
|
|
||||||
void *param; ///< Parameter for the external thread procedure.
|
|
||||||
bool self_destruct; ///< Free ourselves when done?
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a thread and start it, calling proc(param).
|
|
||||||
*/
|
|
||||||
ThreadObject_OS2(OTTDThreadFunc proc, void *param, bool self_destruct) :
|
|
||||||
thread(0),
|
|
||||||
proc(proc),
|
|
||||||
param(param),
|
|
||||||
self_destruct(self_destruct)
|
|
||||||
{
|
|
||||||
thread = _beginthread(stThreadProc, NULL, 1048576, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Exit() override
|
|
||||||
{
|
|
||||||
_endthread();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Join() override
|
|
||||||
{
|
|
||||||
DosWaitThread(&this->thread, DCWW_WAIT);
|
|
||||||
this->thread = 0;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* On thread creation, this function is called, which calls the real startup
|
|
||||||
* function. This to get back into the correct instance again.
|
|
||||||
*/
|
|
||||||
static void stThreadProc(void *thr)
|
|
||||||
{
|
|
||||||
((ThreadObject_OS2 *)thr)->ThreadProc();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new thread is created, and this function is called. Call the custom
|
|
||||||
* function of the creator of the thread.
|
|
||||||
*/
|
|
||||||
void ThreadProc()
|
|
||||||
{
|
|
||||||
/* Call the proc of the creator to continue this thread */
|
|
||||||
try {
|
|
||||||
this->proc(this->param);
|
|
||||||
} catch (OTTDThreadExitSignal e) {
|
|
||||||
} catch (...) {
|
|
||||||
NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self_destruct) {
|
|
||||||
this->Exit();
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
|
|
||||||
{
|
|
||||||
ThreadObject *to = new ThreadObject_OS2(proc, param, thread == NULL);
|
|
||||||
if (thread != NULL) *thread = to;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OS/2 version of ThreadMutex.
|
|
||||||
*/
|
|
||||||
class ThreadMutex_OS2 : public ThreadMutex {
|
|
||||||
private:
|
|
||||||
HMTX mutex; ///< The mutex.
|
|
||||||
HEV event; ///< Event for waiting.
|
|
||||||
uint recursive_count; ///< Recursive lock count.
|
|
||||||
|
|
||||||
public:
|
|
||||||
ThreadMutex_OS2() : recursive_count(0)
|
|
||||||
{
|
|
||||||
DosCreateMutexSem(NULL, &mutex, 0, FALSE);
|
|
||||||
DosCreateEventSem(NULL, &event, 0, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ThreadMutex_OS2() override
|
|
||||||
{
|
|
||||||
DosCloseMutexSem(mutex);
|
|
||||||
DosCloseEventSem(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
/* os2 mutex is recursive by itself */
|
|
||||||
DosRequestMutexSem(mutex, (unsigned long) SEM_INDEFINITE_WAIT);
|
|
||||||
this->recursive_count++;
|
|
||||||
if (!allow_recursive && this->recursive_count != 1) NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EndCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
if (!allow_recursive && this->recursive_count != 1) NOT_REACHED();
|
|
||||||
this->recursive_count--;
|
|
||||||
DosReleaseMutexSem(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitForSignal() override
|
|
||||||
{
|
|
||||||
assert(this->recursive_count == 1); // Do we need to call Begin/EndCritical multiple times otherwise?
|
|
||||||
this->EndCritical();
|
|
||||||
DosWaitEventSem(event, SEM_INDEFINITE_WAIT);
|
|
||||||
this->BeginCritical();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendSignal() override
|
|
||||||
{
|
|
||||||
DosPostEventSem(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ ThreadMutex *ThreadMutex::New()
|
|
||||||
{
|
|
||||||
return new ThreadMutex_OS2();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetSelfAsMainThread() { }
|
|
||||||
|
|
||||||
bool IsMainThread() { return false; }
|
|
||||||
|
|
||||||
bool IsNonMainThread() { return false; }
|
|
||||||
|
|
||||||
int GetThreadName(char *str, const char *last) { return 0; }
|
|
@ -1,223 +0,0 @@
|
|||||||
/* $Id$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of OpenTTD.
|
|
||||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
||||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */
|
|
||||||
|
|
||||||
#include "../stdafx.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "../string_func.h"
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#include "../os/macosx/macos.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../safeguards.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POSIX pthread version for ThreadObject.
|
|
||||||
*/
|
|
||||||
class ThreadObject_pthread : public ThreadObject {
|
|
||||||
private:
|
|
||||||
pthread_t thread; ///< System thread identifier.
|
|
||||||
OTTDThreadFunc proc; ///< External thread procedure.
|
|
||||||
void *param; ///< Parameter for the external thread procedure.
|
|
||||||
bool self_destruct; ///< Free ourselves when done?
|
|
||||||
const char *name; ///< Name for the thread
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a pthread and start it, calling proc(param).
|
|
||||||
*/
|
|
||||||
ThreadObject_pthread(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) :
|
|
||||||
thread(0),
|
|
||||||
proc(proc),
|
|
||||||
param(param),
|
|
||||||
self_destruct(self_destruct),
|
|
||||||
name(name)
|
|
||||||
{
|
|
||||||
pthread_create(&this->thread, NULL, &stThreadProc, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Exit() override
|
|
||||||
{
|
|
||||||
assert(pthread_self() == this->thread);
|
|
||||||
/* For now we terminate by throwing an error, gives much cleaner cleanup */
|
|
||||||
throw OTTDThreadExitSignal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Join() override
|
|
||||||
{
|
|
||||||
/* You cannot join yourself */
|
|
||||||
assert(pthread_self() != this->thread);
|
|
||||||
pthread_join(this->thread, NULL);
|
|
||||||
this->thread = 0;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* On thread creation, this function is called, which calls the real startup
|
|
||||||
* function. This to get back into the correct instance again.
|
|
||||||
*/
|
|
||||||
static void *stThreadProc(void *thr)
|
|
||||||
{
|
|
||||||
ThreadObject_pthread *self = (ThreadObject_pthread *) thr;
|
|
||||||
#if defined(__GLIBC__)
|
|
||||||
#if __GLIBC_PREREQ(2, 12)
|
|
||||||
if (self->name) {
|
|
||||||
pthread_setname_np(pthread_self(), self->name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
MacOSSetThreadName(self->name);
|
|
||||||
#endif
|
|
||||||
self->ThreadProc();
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new thread is created, and this function is called. Call the custom
|
|
||||||
* function of the creator of the thread.
|
|
||||||
*/
|
|
||||||
void ThreadProc()
|
|
||||||
{
|
|
||||||
/* Call the proc of the creator to continue this thread */
|
|
||||||
try {
|
|
||||||
this->proc(this->param);
|
|
||||||
} catch (OTTDThreadExitSignal) {
|
|
||||||
} catch (...) {
|
|
||||||
NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self_destruct) {
|
|
||||||
pthread_detach(pthread_self());
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
|
|
||||||
{
|
|
||||||
ThreadObject *to = new ThreadObject_pthread(proc, param, thread == NULL, name);
|
|
||||||
if (thread != NULL) *thread = to;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POSIX pthread version of ThreadMutex.
|
|
||||||
*/
|
|
||||||
class ThreadMutex_pthread : public ThreadMutex {
|
|
||||||
private:
|
|
||||||
pthread_mutex_t mutex; ///< The actual mutex.
|
|
||||||
pthread_cond_t condition; ///< Data for conditional waiting.
|
|
||||||
pthread_mutexattr_t attr; ///< Attributes set for the mutex.
|
|
||||||
pthread_t owner; ///< Owning thread of the mutex.
|
|
||||||
uint recursive_count; ///< Recursive lock count.
|
|
||||||
|
|
||||||
public:
|
|
||||||
ThreadMutex_pthread() : owner(0), recursive_count(0)
|
|
||||||
{
|
|
||||||
pthread_mutexattr_init(&this->attr);
|
|
||||||
pthread_mutexattr_settype(&this->attr, PTHREAD_MUTEX_ERRORCHECK);
|
|
||||||
pthread_mutex_init(&this->mutex, &this->attr);
|
|
||||||
pthread_cond_init(&this->condition, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ThreadMutex_pthread() override
|
|
||||||
{
|
|
||||||
int err = pthread_cond_destroy(&this->condition);
|
|
||||||
assert(err != EBUSY);
|
|
||||||
err = pthread_mutex_destroy(&this->mutex);
|
|
||||||
assert(err != EBUSY);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsOwnedByCurrentThread() const
|
|
||||||
{
|
|
||||||
return this->owner == pthread_self();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
/* pthread mutex is not recursive by itself */
|
|
||||||
if (this->IsOwnedByCurrentThread()) {
|
|
||||||
if (!allow_recursive) NOT_REACHED();
|
|
||||||
} else {
|
|
||||||
int err = pthread_mutex_lock(&this->mutex);
|
|
||||||
assert(err == 0);
|
|
||||||
assert(this->recursive_count == 0);
|
|
||||||
this->owner = pthread_self();
|
|
||||||
}
|
|
||||||
this->recursive_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EndCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
assert(this->IsOwnedByCurrentThread());
|
|
||||||
if (!allow_recursive && this->recursive_count != 1) NOT_REACHED();
|
|
||||||
this->recursive_count--;
|
|
||||||
if (this->recursive_count != 0) return;
|
|
||||||
this->owner = 0;
|
|
||||||
int err = pthread_mutex_unlock(&this->mutex);
|
|
||||||
assert(err == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitForSignal() override
|
|
||||||
{
|
|
||||||
uint old_recursive_count = this->recursive_count;
|
|
||||||
this->recursive_count = 0;
|
|
||||||
this->owner = 0;
|
|
||||||
int err = pthread_cond_wait(&this->condition, &this->mutex);
|
|
||||||
assert(err == 0);
|
|
||||||
this->owner = pthread_self();
|
|
||||||
this->recursive_count = old_recursive_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendSignal() override
|
|
||||||
{
|
|
||||||
int err = pthread_cond_signal(&this->condition);
|
|
||||||
assert(err == 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ ThreadMutex *ThreadMutex::New()
|
|
||||||
{
|
|
||||||
return new ThreadMutex_pthread();
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_t main_thread;
|
|
||||||
|
|
||||||
void SetSelfAsMainThread()
|
|
||||||
{
|
|
||||||
main_thread = pthread_self();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsMainThread()
|
|
||||||
{
|
|
||||||
return main_thread == pthread_self();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsNonMainThread()
|
|
||||||
{
|
|
||||||
return main_thread != pthread_self();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetThreadName(char *str, const char *last)
|
|
||||||
{
|
|
||||||
#if defined(__GLIBC__)
|
|
||||||
#if __GLIBC_PREREQ(2, 12)
|
|
||||||
char buffer[16];
|
|
||||||
int result = pthread_getname_np(pthread_self(), buffer, sizeof(buffer));
|
|
||||||
if (result == 0) {
|
|
||||||
return seprintf(str, last, "%s", buffer);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,214 +0,0 @@
|
|||||||
/* $Id$ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of OpenTTD.
|
|
||||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
||||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file thread_win32.cpp Win32 thread implementation of Threads. */
|
|
||||||
|
|
||||||
#include "../stdafx.h"
|
|
||||||
#include "thread.h"
|
|
||||||
#include "../debug.h"
|
|
||||||
#include "../core/alloc_func.hpp"
|
|
||||||
#include "../scope.h"
|
|
||||||
#include "../string_func.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <process.h>
|
|
||||||
#include "../os/windows/win32.h"
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "../safeguards.h"
|
|
||||||
|
|
||||||
static void Win32SetThreadName(uint id, const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Win32 thread version for ThreadObject.
|
|
||||||
*/
|
|
||||||
class ThreadObject_Win32 : public ThreadObject {
|
|
||||||
private:
|
|
||||||
HANDLE thread; ///< System thread identifier.
|
|
||||||
uint id; ///< Thread identifier.
|
|
||||||
OTTDThreadFunc proc; ///< External thread procedure.
|
|
||||||
void *param; ///< Parameter for the external thread procedure.
|
|
||||||
bool self_destruct; ///< Free ourselves when done?
|
|
||||||
const char *name; ///< Thread name.
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a win32 thread and start it, calling proc(param).
|
|
||||||
*/
|
|
||||||
ThreadObject_Win32(OTTDThreadFunc proc, void *param, bool self_destruct, const char *name) :
|
|
||||||
thread(NULL),
|
|
||||||
id(0),
|
|
||||||
proc(proc),
|
|
||||||
param(param),
|
|
||||||
self_destruct(self_destruct),
|
|
||||||
name(name)
|
|
||||||
{
|
|
||||||
this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id);
|
|
||||||
if (this->thread == NULL) return;
|
|
||||||
Win32SetThreadName(this->id, name);
|
|
||||||
ResumeThread(this->thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ThreadObject_Win32() override
|
|
||||||
{
|
|
||||||
if (this->thread != NULL) {
|
|
||||||
CloseHandle(this->thread);
|
|
||||||
this->thread = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Exit() override
|
|
||||||
{
|
|
||||||
assert(GetCurrentThreadId() == this->id);
|
|
||||||
/* For now we terminate by throwing an error, gives much cleaner cleanup */
|
|
||||||
throw OTTDThreadExitSignal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Join() override
|
|
||||||
{
|
|
||||||
/* You cannot join yourself */
|
|
||||||
assert(GetCurrentThreadId() != this->id);
|
|
||||||
WaitForSingleObject(this->thread, INFINITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* On thread creation, this function is called, which calls the real startup
|
|
||||||
* function. This to get back into the correct instance again.
|
|
||||||
*/
|
|
||||||
static uint CALLBACK stThreadProc(void *thr)
|
|
||||||
{
|
|
||||||
((ThreadObject_Win32 *)thr)->ThreadProc();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A new thread is created, and this function is called. Call the custom
|
|
||||||
* function of the creator of the thread.
|
|
||||||
*/
|
|
||||||
void ThreadProc()
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
/* Set thread name for debuggers. Has to be done from the thread due to a race condition in older MS debuggers. */
|
|
||||||
SetWin32ThreadName(-1, this->name);
|
|
||||||
#endif
|
|
||||||
try {
|
|
||||||
this->proc(this->param);
|
|
||||||
} catch (OTTDThreadExitSignal) {
|
|
||||||
} catch (...) {
|
|
||||||
NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self_destruct) delete this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
|
|
||||||
{
|
|
||||||
ThreadObject *to = new ThreadObject_Win32(proc, param, thread == NULL, name);
|
|
||||||
if (thread != NULL) *thread = to;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Win32 thread version of ThreadMutex.
|
|
||||||
*/
|
|
||||||
class ThreadMutex_Win32 : public ThreadMutex {
|
|
||||||
private:
|
|
||||||
CRITICAL_SECTION critical_section; ///< The critical section we would enter.
|
|
||||||
HANDLE event; ///< Event for signalling.
|
|
||||||
uint recursive_count; ///< Recursive lock count.
|
|
||||||
|
|
||||||
public:
|
|
||||||
ThreadMutex_Win32() : recursive_count(0)
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&this->critical_section);
|
|
||||||
this->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ThreadMutex_Win32() override
|
|
||||||
{
|
|
||||||
DeleteCriticalSection(&this->critical_section);
|
|
||||||
CloseHandle(this->event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
/* windows mutex is recursive by itself */
|
|
||||||
EnterCriticalSection(&this->critical_section);
|
|
||||||
this->recursive_count++;
|
|
||||||
if (!allow_recursive && this->recursive_count != 1) NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EndCritical(bool allow_recursive = false) override
|
|
||||||
{
|
|
||||||
if (!allow_recursive && this->recursive_count != 1) NOT_REACHED();
|
|
||||||
this->recursive_count--;
|
|
||||||
LeaveCriticalSection(&this->critical_section);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitForSignal() override
|
|
||||||
{
|
|
||||||
assert(this->recursive_count == 1); // Do we need to call Begin/EndCritical multiple times otherwise?
|
|
||||||
this->EndCritical();
|
|
||||||
WaitForSingleObject(this->event, INFINITE);
|
|
||||||
this->BeginCritical();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendSignal() override
|
|
||||||
{
|
|
||||||
SetEvent(this->event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* static */ ThreadMutex *ThreadMutex::New()
|
|
||||||
{
|
|
||||||
return new ThreadMutex_Win32();
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint main_thread_id;
|
|
||||||
|
|
||||||
void SetSelfAsMainThread()
|
|
||||||
{
|
|
||||||
main_thread_id = GetCurrentThreadId();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsMainThread()
|
|
||||||
{
|
|
||||||
return main_thread_id == GetCurrentThreadId();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsNonMainThread()
|
|
||||||
{
|
|
||||||
return main_thread_id != GetCurrentThreadId();
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::map<uint, std::string> _thread_name_map;
|
|
||||||
static ThreadMutex_Win32 _thread_name_map_mutex;
|
|
||||||
|
|
||||||
static void Win32SetThreadName(uint id, const char *name)
|
|
||||||
{
|
|
||||||
_thread_name_map_mutex.BeginCritical();
|
|
||||||
_thread_name_map[id] = name;
|
|
||||||
_thread_name_map_mutex.EndCritical();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetThreadName(char *str, const char *last)
|
|
||||||
{
|
|
||||||
_thread_name_map_mutex.BeginCritical();
|
|
||||||
auto guard = scope_guard([&]() {
|
|
||||||
_thread_name_map_mutex.EndCritical();
|
|
||||||
});
|
|
||||||
auto iter = _thread_name_map.find(GetCurrentThreadId());
|
|
||||||
if (iter != _thread_name_map.end()) {
|
|
||||||
return seprintf(str, last, "%s", iter->second.c_str());
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue