diff options
author | kwolekr <kwolekr@minetest.net> | 2015-10-16 23:43:29 -0400 |
---|---|---|
committer | kwolekr <kwolekr@minetest.net> | 2015-10-16 23:43:29 -0400 |
commit | 765a834cd04473afeb4b86b445dee901e0d0c83c (patch) | |
tree | b18e7147fb96fc6b352b1c52690315e82b36de82 /src/threading/thread.cpp | |
parent | 6be74d17df75714066b36cfa6ae40081526ef477 (diff) | |
download | minetest-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/thread.cpp')
-rw-r--r-- | src/threading/thread.cpp | 390 |
1 files changed, 245 insertions, 145 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 } |