aboutsummaryrefslogtreecommitdiff
path: root/src/threading
diff options
context:
space:
mode:
authorkwolekr <kwolekr@minetest.net>2015-10-16 23:43:29 -0400
committerkwolekr <kwolekr@minetest.net>2015-10-16 23:43:29 -0400
commit765a834cd04473afeb4b86b445dee901e0d0c83c (patch)
treeb18e7147fb96fc6b352b1c52690315e82b36de82 /src/threading
parent6be74d17df75714066b36cfa6ae40081526ef477 (diff)
downloadminetest-765a834cd04473afeb4b86b445dee901e0d0c83c.tar.gz
minetest-765a834cd04473afeb4b86b445dee901e0d0c83c.tar.bz2
minetest-765a834cd04473afeb4b86b445dee901e0d0c83c.zip
Refactor Thread class to improve readability and portability
- Fix some incompatibilities with obscure platforms (AIX and WinCE) - Clean up Thread class interface - Add m_ prefix to private member variables - Simplify platform-dependent logic, reducing preprocessor conditional clauses and improving readibility - Add Thread class documentation
Diffstat (limited to 'src/threading')
-rw-r--r--src/threading/thread.cpp390
-rw-r--r--src/threading/thread.h161
2 files changed, 348 insertions, 203 deletions
diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp
index 1f9b54795..a10082478 100644
--- a/src/threading/thread.cpp
+++ b/src/threading/thread.cpp
@@ -26,48 +26,48 @@ DEALINGS IN THE SOFTWARE.
#include "threading/thread.h"
#include "threading/mutex_auto_lock.h"
#include "log.h"
+#include "porting.h"
-#if __cplusplus >= 201103L
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+#if USE_CPP11_THREADS
#include <chrono>
-#else
- #define UNUSED(expr) do { (void)(expr); } while (0)
-# ifdef _WIN32
-# ifndef _WIN32_WCE
- #include <process.h>
-# endif
-# else
- #include <ctime>
- #include <cassert>
- #include <cstdlib>
+ #include <system_error>
+#elif USE_WIN_THREADS
+ #ifndef _WIN32_WCE
+ #include <process.h>
+ #endif
+#elif USE_POSIX_THREADS
+ #include <time.h>
+ #include <assert.h>
+ #include <stdlib.h>
+ #include <unistd.h>
#include <sys/time.h>
- // For getNumberOfProcessors
- #include <unistd.h>
-# if defined(__FreeBSD__) || defined(__APPLE__)
- #include <sys/types.h>
- #include <sys/sysctl.h>
-# elif defined(_GNU_SOURCE)
- #include <sys/sysinfo.h>
-# endif
-# endif
+ #if defined(__FreeBSD__) || defined(__APPLE__)
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+ #elif defined(_GNU_SOURCE)
+ #include <sys/sysinfo.h>
+ #endif
#endif
-// For setName
+// for setName
#if defined(linux) || defined(__linux)
#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h>
#elif defined(_MSC_VER)
struct THREADNAME_INFO {
- DWORD dwType; // Must be 0x1000
- LPCSTR szName; // Pointer to name (in user addr space)
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user addr space)
DWORD dwThreadID; // Thread ID (-1=caller thread)
- DWORD dwFlags; // Reserved for future use, must be zero
+ DWORD dwFlags; // Reserved for future use, must be zero
};
#endif
-// For bindToProcessor
+// for bindToProcessor
#if __FreeBSD_version >= 702106
typedef cpuset_t cpu_set_t;
#elif defined(__linux) || defined(linux)
@@ -78,6 +78,7 @@ DEALINGS IN THE SOFTWARE.
#include <sys/procset.h>
#elif defined(_AIX)
#include <sys/processor.h>
+ #include <sys/thread.h>
#elif defined(__APPLE__)
#include <mach/mach_init.h>
#include <mach/thread_act.h>
@@ -85,188 +86,246 @@ DEALINGS IN THE SOFTWARE.
Thread::Thread(const std::string &name) :
- name(name),
- retval(NULL),
- request_stop(false),
- running(false)
-#if __cplusplus >= 201103L
- , thread(NULL)
-#elif !defined(_WIN32)
- , started(false)
+ m_name(name),
+ m_retval(NULL),
+ m_request_stop(false),
+ m_running(false)
+{
+#ifdef _AIX
+ m_kernel_thread_id = -1;
#endif
-{}
+#if USE_CPP11_THREADS
+ m_thread_obj = NULL;
+#endif
+}
-void Thread::wait()
+
+Thread::~Thread()
{
-#if __cplusplus >= 201103L
- if (!thread || !thread->joinable())
- return;
- thread->join();
-#elif defined(_WIN32)
- if (!running)
- return;
- WaitForSingleObject(thread, INFINITE);
-#else // pthread
- void *status;
- if (!started)
- return;
- int ret = pthread_join(thread, &status);
- assert(!ret);
- UNUSED(ret);
- started = false;
-#endif
+ kill();
}
bool Thread::start()
{
- if (running)
+ MutexAutoLock lock(m_continue_mutex);
+
+ if (m_running)
return false;
- request_stop = false;
-#if __cplusplus >= 201103L
- MutexAutoLock l(continue_mutex);
- thread = new std::thread(theThread, this);
-#elif defined(_WIN32)
- MutexAutoLock l(continue_mutex);
-# ifdef _WIN32_WCE
- thread = CreateThread(NULL, 0, theThread, this, 0, &thread_id);
-# else
- thread = (HANDLE)_beginthreadex(NULL, 0, theThread, this, 0, &thread_id);
-# endif
- if (!thread)
+ cleanup();
+
+#if USE_CPP11_THREADS
+
+ try {
+ m_thread_obj = new std::thread(threadProc, this);
+ m_thread_id = m_thread->get_id();
+ m_thread_handle = m_thread->native_handle();
+ } except (const std::system_error &e) {
return false;
-#else
- int status;
+ }
- MutexAutoLock l(continue_mutex);
+#elif USE_WIN_THREADS
- status = pthread_create(&thread, NULL, theThread, this);
+ m_thread_handle = CreateThread(NULL, 0, threadProc, this, 0, &m_thread_id);
+ if (!m_thread_handle)
+ return false;
+
+#elif USE_POSIX_THREADS
+ int status = pthread_create(&m_thread_handle, NULL, threadProc, this);
if (status)
return false;
-#endif
-#if __cplusplus < 201103L
- // Wait until running
- while (!running) {
-# ifdef _WIN32
- Sleep(1);
- }
-# else
- struct timespec req, rem;
- req.tv_sec = 0;
- req.tv_nsec = 1000000;
- nanosleep(&req, &rem);
- }
- started = true;
-# endif
+ m_thread_id = m_thread_handle;
+
#endif
+
+ while (!m_running)
+ sleep_ms(1);
+
+ return true;
+}
+
+
+bool Thread::stop()
+{
+ m_request_stop = true;
return true;
}
+void Thread::wait()
+{
+ if (!m_running)
+ return;
+
+#if USE_CPP11_THREADS
+
+ m_thread_obj->join();
+
+#elif USE_WIN_THREADS
+
+ int ret == WaitForSingleObject(m_thread_handle, INFINITE);
+ assert(ret == WAIT_OBJECT_0);
+ UNUSED(ret);
+
+#elif USE_POSIX_THREADS
+
+ int ret = pthread_join(m_thread_handle, NULL);
+ assert(ret == 0);
+ UNUSED(ret);
+
+#endif
+
+ assert(m_running == false);
+
+ return;
+}
+
+
bool Thread::kill()
{
-#ifdef _WIN32
- if (!running)
- return false;
- TerminateThread(getThreadHandle(), 0);
- CloseHandle(getThreadHandle());
-#else
- if (!running) {
+ if (!m_running) {
wait();
return false;
}
+
+#ifdef _WIN32
+ TerminateThread(m_thread_handle, 0);
+#else
+
+ // We need to pthread_kill instead on Android since NDKv5's pthread
+ // implementation is incomplete.
# ifdef __ANDROID__
- pthread_kill(getThreadHandle(), SIGKILL);
+ pthread_kill(m_thread_handle, SIGKILL);
# else
- pthread_cancel(getThreadHandle());
+ pthread_cancel(m_thread_handle);
# endif
+
wait();
#endif
-#if __cplusplus >= 201103L
- delete thread;
-#endif
- running = false;
+
+ cleanup();
+
return true;
}
-bool Thread::isSameThread()
+void Thread::cleanup()
{
-#if __cplusplus >= 201103L
- return thread->get_id() == std::this_thread::get_id();
-#elif defined(_WIN32)
- return GetCurrentThreadId() == thread_id;
-#else
- return pthread_equal(pthread_self(), thread);
+#if USE_CPP11_THREADS
+
+ delete m_thread_obj;
+ m_thread_obj = NULL;
+
+#elif USE_WIN_THREADS
+
+ CloseHandle(m_thread_handle);
+ m_thread_handle = NULL;
+ m_thread_id = -1;
+
+#elif USE_POSIX_THREADS
+
+ // Can't do any cleanup for pthreads
+
#endif
+
+ m_name = "";
+ m_retval = NULL;
+ m_running = false;
+ m_request_stop = false;
}
-#if __cplusplus >= 201103L
-void Thread::theThread(Thread *th)
+bool Thread::getReturnValue(void **ret)
+{
+ if (m_running)
+ return false;
+
+ *ret = m_retval;
+ return true;
+}
+
+
+bool Thread::isCurrentThread()
+{
+ return thr_is_current_thread(m_thread_id);
+}
+
+
+#if USE_CPP11_THREADS || USE_POSIX_THREADS
+ void *(Thread::threadProc)(void *param)
#elif defined(_WIN32_WCE)
-DWORD WINAPI Thread::theThread(void *param)
+ DWORD (Thread::threadProc)(LPVOID param)
#elif defined(_WIN32)
-UINT __stdcall Thread::theThread(void *param)
-#else
-void *Thread::theThread(void *param)
+ DWORD WINAPI (Thread::threadProc)(LPVOID param)
#endif
{
-#if __cplusplus < 201103L
- Thread *th = static_cast<Thread *>(param);
+ Thread *thr = (Thread *)param;
+
+#ifdef _AIX
+ m_kernel_thread_id = thread_self();
#endif
- th->running = true;
- th->setName();
- g_logger.registerThread(th->name);
+ thr->setName(thr->m_name);
+
+ g_logger.registerThread(thr->m_name);
+ thr->m_running = true;
- th->retval = th->run();
+ thr->m_retval = thr->run();
+ thr->m_running = false;
g_logger.deregisterThread();
- th->running = false;
-#if __cplusplus < 201103L
-# ifdef _WIN32
- CloseHandle(th->thread);
-# endif
return NULL;
-#endif
}
void Thread::setName(const std::string &name)
{
#if defined(linux) || defined(__linux)
- /* It would be cleaner to do this with pthread_setname_np,
- * which was added to glibc in version 2.12, but some major
- * distributions are still runing 2.11 and previous versions.
- */
+
+ // It would be cleaner to do this with pthread_setname_np,
+ // which was added to glibc in version 2.12, but some major
+ // distributions are still runing 2.11 and previous versions.
prctl(PR_SET_NAME, name.c_str());
+
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+
pthread_set_name_np(pthread_self(), name.c_str());
+
#elif defined(__NetBSD__)
+
pthread_setname_np(pthread_self(), name.c_str());
+
#elif defined(__APPLE__)
+
pthread_setname_np(name.c_str());
+
#elif defined(_MSC_VER)
+
// Windows itself doesn't support thread names,
// but the MSVC debugger does...
THREADNAME_INFO info;
+
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = -1;
info.dwFlags = 0;
+
__try {
- RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+ RaiseException(0x406D1388, 0,
+ sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
} __except (EXCEPTION_CONTINUE_EXECUTION) {
}
+
#elif defined(_WIN32) || defined(__GNU__)
+
// These platforms are known to not support thread names.
// Silently ignore the request.
+
#else
#warning "Unrecognized platform, thread names will not be available."
#endif
@@ -276,59 +335,96 @@ void Thread::setName(const std::string &name)
unsigned int Thread::getNumberOfProcessors()
{
#if __cplusplus >= 201103L
+
return std::thread::hardware_concurrency();
+
#elif defined(_SC_NPROCESSORS_ONLN)
+
return sysconf(_SC_NPROCESSORS_ONLN);
-#elif defined(__FreeBSD__) || defined(__APPLE__)
- unsigned int len, count;
- len = sizeof(count);
- return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
+
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__DragonFly__) || defined(__APPLE__)
+
+ unsigned int num_cpus = 1;
+ size_t len = sizeof(num_cpus);
+
+ int mib[2];
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+
+ sysctl(mib, 2, &num_cpus, &len, NULL, 0);
+ return num_cpus;
+
#elif defined(_GNU_SOURCE)
+
return get_nprocs();
+
#elif defined(_WIN32)
+
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
+
#elif defined(PTW32_VERSION) || defined(__hpux)
+
return pthread_num_processors_np();
+
#else
+
return 1;
+
#endif
}
-bool Thread::bindToProcessor(unsigned int num)
+bool Thread::bindToProcessor(unsigned int proc_number)
{
#if defined(__ANDROID__)
+
return false;
+
#elif defined(_WIN32)
- return SetThreadAffinityMask(getThreadHandle(), 1 << num);
+
+ return SetThreadAffinityMask(m_thread_handle, 1 << proc_number);
+
#elif __FreeBSD_version >= 702106 || defined(__linux) || defined(linux)
+
cpu_set_t cpuset;
+
CPU_ZERO(&cpuset);
- CPU_SET(num, &cpuset);
- return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset),
- &cpuset) == 0;
+ CPU_SET(proc_number, &cpuset);
+
+ return pthread_setaffinity_np(m_thread_handle, sizeof(cpuset), &cpuset) == 0;
+
#elif defined(__sun) || defined(sun)
- return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(getThreadHandle()),
- num, NULL) == 0
+
+ return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0
+
#elif defined(_AIX)
- return bindprocessor(BINDTHREAD, (tid_t) getThreadHandle(), pnumber) == 0;
+
+ return bindprocessor(BINDTHREAD, m_kernel_thread_id, proc_number) == 0;
+
#elif defined(__hpux) || defined(hpux)
+
pthread_spu_t answer;
return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
- &answer, num, getThreadHandle()) == 0;
+ &answer, proc_number, m_thread_handle) == 0;
+
#elif defined(__APPLE__)
+
struct thread_affinity_policy tapol;
- thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
- tapol.affinity_tag = num + 1;
+ thread_port_t threadport = pthread_mach_thread_np(m_thread_handle);
+ tapol.affinity_tag = proc_number + 1;
return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
(thread_policy_t)&tapol,
THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
+
#else
+
return false;
+
#endif
}
@@ -336,19 +432,23 @@ bool Thread::bindToProcessor(unsigned int num)
bool Thread::setPriority(int prio)
{
#if defined(_WIN32)
- return SetThreadPriority(getThreadHandle(), prio);
+
+ return SetThreadPriority(m_thread_handle, prio);
+
#else
+
struct sched_param sparam;
int policy;
- if (pthread_getschedparam(getThreadHandle(), &policy, &sparam) != 0)
+ if (pthread_getschedparam(m_thread_handle, &policy, &sparam) != 0)
return false;
int min = sched_get_priority_min(policy);
int max = sched_get_priority_max(policy);
sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
- return pthread_setschedparam(getThreadHandle(), policy, &sparam) == 0;
+ return pthread_setschedparam(m_thread_handle, policy, &sparam) == 0;
+
#endif
}
diff --git a/src/threading/thread.h b/src/threading/thread.h
index 275bc9b6d..3c8447b53 100644
--- a/src/threading/thread.h
+++ b/src/threading/thread.h
@@ -28,91 +28,136 @@ DEALINGS IN THE SOFTWARE.
#include "threading/atomic.h"
#include "threading/mutex.h"
+#include "threads.h"
#include <string>
-#if __cplusplus >= 201103L
+#if USE_CPP11_THREADS
#include <thread>
#endif
-#ifndef _WIN32
-enum {
- THREAD_PRIORITY_LOWEST,
- THREAD_PRIORITY_BELOW_NORMAL,
- THREAD_PRIORITY_NORMAL,
- THREAD_PRIORITY_ABOVE_NORMAL,
- THREAD_PRIORITY_HIGHEST,
-};
+/*
+ * On platforms using pthreads, these five priority classes correlate to
+ * even divisions between the minimum and maximum reported thread priority.
+ */
+#if !defined(_WIN32)
+ #define THREAD_PRIORITY_LOWEST 0
+ #define THREAD_PRIORITY_BELOW_NORMAL 1
+ #define THREAD_PRIORITY_NORMAL 2
+ #define THREAD_PRIORITY_ABOVE_NORMAL 3
+ #define THREAD_PRIORITY_HIGHEST 4
#endif
-class Thread
-{
+class Thread {
public:
- Thread(const std::string &name="Unnamed");
- virtual ~Thread() { kill(); }
+ Thread(const std::string &name="");
+ virtual ~Thread();
+ /*
+ * Begins execution of a new thread at the pure virtual method Thread::run().
+ * Execution of the thread is guaranteed to have started after this function
+ * returns.
+ */
bool start();
- inline void stop() { request_stop = true; }
- bool kill();
- inline bool isRunning() { return running; }
- inline bool stopRequested() { return request_stop; }
- void *getReturnValue() { return running ? NULL : retval; }
- bool isSameThread();
+ /*
+ * Requests that the thread exit gracefully.
+ * Returns immediately; thread execution is guaranteed to be complete after
+ * a subsequent call to Thread::wait.
+ */
+ bool stop();
- static unsigned int getNumberOfProcessors();
- bool bindToProcessor(unsigned int);
- bool setPriority(int);
+ /*
+ * Immediately terminates the thread.
+ * This should be used with extreme caution, as the thread will not have
+ * any opportunity to release resources it may be holding (such as memory
+ * or locks).
+ */
+ bool kill();
/*
- * Wait for thread to finish.
- * Note: this does not stop a thread, you have to do this on your own.
+ * Waits for thread to finish.
+ * Note: This does not stop a thread, you have to do this on your own.
* Returns immediately if the thread is not started.
*/
void wait();
+ /*
+ * Returns true if the calling thread is this Thread object.
+ */
+ bool isCurrentThread();
+
+ inline bool isRunning() { return m_running; }
+ inline bool stopRequested() { return m_request_stop; }
+ inline threadid_t getThreadId() { return m_thread_id; }
+ inline threadhandle_t getThreadHandle() { return m_thread_handle; }
+
+ /*
+ * Gets the thread return value.
+ * Returns true if the thread has exited and the return value was available,
+ * or false if the thread has yet to finish.
+ */
+ bool getReturnValue(void **ret);
+
+ /*
+ * Binds (if possible, otherwise sets the affinity of) the thread to the
+ * specific processor specified by proc_number.
+ */
+ bool bindToProcessor(unsigned int proc_number);
+
+ /*
+ * Sets the thread priority to the specified priority.
+ *
+ * prio can be one of: THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL,
+ * THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST.
+ * On Windows, any of the other priorites as defined by SetThreadPriority
+ * are supported as well.
+ *
+ * Note that it may be necessary to first set the threading policy or
+ * scheduling algorithm to one that supports thread priorities if not
+ * supported by default, otherwise this call will have no effect.
+ */
+ bool setPriority(int prio);
+
+ /*
+ * Sets the currently executing thread's name to where supported; useful
+ * for debugging.
+ */
static void setName(const std::string &name);
+ /*
+ * Returns the number of processors/cores configured and active on this machine.
+ */
+ static unsigned int getNumberOfProcessors();
+
protected:
- std::string name;
+ std::string m_name;
virtual void *run() = 0;
private:
- void setName() { setName(name); }
-
- void *retval;
- Atomic<bool> request_stop;
- Atomic<bool> running;
- Mutex continue_mutex;
-
-#if __cplusplus >= 201103L
- static void theThread(Thread *th);
-
- std::thread *thread;
- std::thread::native_handle_type getThreadHandle() const
- { return thread->native_handle(); }
-#else
-# if defined(WIN32) || defined(_WIN32_WCE)
-# ifdef _WIN32_WCE
- DWORD thread_id;
- static DWORD WINAPI theThread(void *param);
-# else
- UINT thread_id;
- static UINT __stdcall theThread(void *param);
-# endif
-
- HANDLE thread;
- HANDLE getThreadHandle() const { return thread; }
-# else // pthread
- static void *theThread(void *param);
-
- pthread_t thread;
- pthread_t getThreadHandle() const { return thread; }
-
- Atomic<bool> started;
-# endif
+ void *m_retval;
+ Atomic<bool> m_request_stop;
+ Atomic<bool> m_running;
+ Mutex m_continue_mutex;
+
+ threadid_t m_thread_id;
+ threadhandle_t m_thread_handle;
+
+ void cleanup();
+
+ static ThreadStartFunc threadProc;
+
+#ifdef _AIX
+ // For AIX, there does not exist any mapping from pthread_t to tid_t
+ // available to us, so we maintain one ourselves. This is set on thread start.
+ tid_t m_kernel_thread_id;
+#endif
+
+#if USE_CPP11_THREADS
+ std::thread *m_thread_obj;
#endif
+
};
#endif