aboutsummaryrefslogtreecommitdiff
path: root/src/threading
diff options
context:
space:
mode:
authorShadowNinja <shadowninja@minetest.net>2015-04-07 06:13:12 -0400
committerShadowNinja <shadowninja@minetest.net>2015-08-23 22:04:06 -0400
commite4bff8be94c0db4f94e63ad448d0eeb869ccdbbd (patch)
tree7935586e79da5c8c7144e345a8c0fc1cda53beed /src/threading
parent6a1047d8c116f793890b63427d53f04ceca95d54 (diff)
downloadminetest-e4bff8be94c0db4f94e63ad448d0eeb869ccdbbd.tar.gz
minetest-e4bff8be94c0db4f94e63ad448d0eeb869ccdbbd.tar.bz2
minetest-e4bff8be94c0db4f94e63ad448d0eeb869ccdbbd.zip
Clean up threading
* Rename everything. * Strip J prefix. * Change UpperCamelCase functions to lowerCamelCase. * Remove global (!) semaphore count mutex on OSX. * Remove semaphore count getter (unused, unsafe, depended on internal API functions on Windows, and used a hack on OSX). * Add `Atomic<type>`. * Make `Thread` handle thread names. * Add support for C++11 multi-threading. * Combine pthread and win32 sources. * Remove `ThreadStarted` (unused, unneeded). * Move some includes from the headers to the sources. * Move all of `Event` into its header (allows inlining with no new includes). * Make `Event` use `Semaphore` (except on Windows). * Move some porting functions into `Thread`. * Integrate logging with `Thread`. * Add threading test.
Diffstat (limited to 'src/threading')
-rw-r--r--src/threading/CMakeLists.txt6
-rw-r--r--src/threading/atomic.h96
-rw-r--r--src/threading/event.h57
-rw-r--r--src/threading/mutex.cpp83
-rw-r--r--src/threading/mutex.h66
-rw-r--r--src/threading/mutex_auto_lock.h50
-rw-r--r--src/threading/semaphore.cpp161
-rw-r--r--src/threading/semaphore.h52
-rw-r--r--src/threading/thread.cpp354
-rw-r--r--src/threading/thread.h119
10 files changed, 1044 insertions, 0 deletions
diff --git a/src/threading/CMakeLists.txt b/src/threading/CMakeLists.txt
new file mode 100644
index 000000000..f3d0efc18
--- /dev/null
+++ b/src/threading/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(JTHREAD_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/mutex.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/thread.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/semaphore.cpp
+ PARENT_SCOPE)
+
diff --git a/src/threading/atomic.h b/src/threading/atomic.h
new file mode 100644
index 000000000..486bc7950
--- /dev/null
+++ b/src/threading/atomic.h
@@ -0,0 +1,96 @@
+/*
+Minetest
+Copyright (C) 2015 ShadowNinja <shadowninja@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef THREADING_ATOMIC_H
+#define THREADING_ATOMIC_H
+
+
+#if __cplusplus >= 201103L
+ #include <atomic>
+ template<typename T> using Atomic = std::atomic<T>;
+#else
+
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#define CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#if GCC_VERSION >= 407 || CLANG_VERSION >= 302
+ #define ATOMIC_LOAD(T, v) return __atomic_load_n(&(v), __ATOMIC_SEQ_CST)
+ #define ATOMIC_STORE(T, v, x) __atomic_store (&(v), &(x), __ATOMIC_SEQ_CST); return x
+ #define ATOMIC_ADD_EQ(T, v, x) return __atomic_add_fetch(&(v), (x), __ATOMIC_SEQ_CST)
+ #define ATOMIC_SUB_EQ(T, v, x) return __atomic_sub_fetch(&(v), (x), __ATOMIC_SEQ_CST)
+ #define ATOMIC_POST_INC(T, v) return __atomic_fetch_add(&(v), 1, __ATOMIC_SEQ_CST)
+ #define ATOMIC_POST_DEC(T, v) return __atomic_fetch_sub(&(v), 1, __ATOMIC_SEQ_CST)
+#else
+ #define ATOMIC_USE_LOCK
+ #include "threading/mutex.h"
+
+ #define ATOMIC_LOCK_OP(T, op) do { \
+ mutex.lock(); \
+ T _val = (op); \
+ mutex.unlock(); \
+ return _val; \
+ } while (0)
+ #define ATOMIC_LOAD(T, v) \
+ if (sizeof(T) <= sizeof(void*)) return v; \
+ else ATOMIC_LOCK_OP(T, v);
+ #define ATOMIC_STORE(T, v, x) \
+ if (sizeof(T) <= sizeof(void*)) return v = x; \
+ else ATOMIC_LOCK_OP(T, v = x);
+# if GCC_VERSION >= 401
+ #define ATOMIC_ADD_EQ(T, v, x) return __sync_add_and_fetch(&(v), (x))
+ #define ATOMIC_SUB_EQ(T, v, x) return __sync_sub_and_fetch(&(v), (x))
+ #define ATOMIC_POST_INC(T, v) return __sync_fetch_and_add(&(v), 1)
+ #define ATOMIC_POST_DEC(T, v) return __sync_fetch_and_sub(&(v), 1)
+# else
+ #define ATOMIC_ADD_EQ(T, v, x) ATOMIC_LOCK_OP(T, v += x)
+ #define ATOMIC_SUB_EQ(T, v, x) ATOMIC_LOCK_OP(T, v -= x)
+ #define ATOMIC_POST_INC(T, v) ATOMIC_LOCK_OP(T, v++)
+ #define ATOMIC_POST_DEC(T, v) ATOMIC_LOCK_OP(T, v--)
+# endif
+#endif
+
+
+template<typename T>
+class Atomic
+{
+ // Like C++11 std::enable_if, but defaults to char since C++03 doesn't support SFINAE
+ template<bool B, typename T_ = void> struct enable_if { typedef char type; };
+ template<typename T_> struct enable_if<true, T_> { typedef T_ type; };
+public:
+ Atomic(const T &v=0) : val(v) {}
+
+ operator T () { ATOMIC_LOAD(T, val); }
+ T operator = (T x) { ATOMIC_STORE(T, val, x); }
+ T operator += (T x) { ATOMIC_ADD_EQ(T, val, x); }
+ T operator -= (T x) { ATOMIC_SUB_EQ(T, val, x); }
+ T operator ++ () { return *this += 1; }
+ T operator -- () { return *this -= 1; }
+ T operator ++ (int) { ATOMIC_POST_INC(T, val); }
+ T operator -- (int) { ATOMIC_POST_DEC(T, val); }
+
+private:
+ volatile T val;
+#ifdef ATOMIC_USE_LOCK
+ typename enable_if<sizeof(T) <= sizeof(void*), Mutex>::type mutex;
+#endif
+};
+
+#endif // C++11
+
+#endif
+
diff --git a/src/threading/event.h b/src/threading/event.h
new file mode 100644
index 000000000..0105630e5
--- /dev/null
+++ b/src/threading/event.h
@@ -0,0 +1,57 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_EVENT_H
+#define THREADING_EVENT_H
+
+#ifdef _WIN32
+ #include <windows.h>
+#else
+ #include "threading/semaphore.h"
+#endif
+
+
+class Event {
+public:
+#ifdef _WIN32
+ Event() { event = CreateEvent(NULL, false, false, NULL); }
+ ~Event() { CloseHandle(event); }
+ void wait() { WaitForSingleObject(event, INFINITE); }
+ void signal() { SetEvent(event); }
+#else
+ void wait() { sem.wait(); }
+ void signal() { sem.post(); }
+#endif
+
+private:
+#ifdef _WIN32
+ HANDLE event;
+#else
+ Semaphore sem;
+#endif
+};
+
+#endif
+
diff --git a/src/threading/mutex.cpp b/src/threading/mutex.cpp
new file mode 100644
index 000000000..eb1c7d61d
--- /dev/null
+++ b/src/threading/mutex.cpp
@@ -0,0 +1,83 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// Windows std::mutex is much slower than the critical section API
+#if __cplusplus < 201103L || defined(_WIN32)
+
+#include "threading/mutex.h"
+
+#ifndef _WIN32
+ #include <cassert>
+#endif
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+
+Mutex::Mutex()
+{
+#ifdef _WIN32
+ InitializeCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_init(&mutex, NULL);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+Mutex::~Mutex()
+{
+#ifdef _WIN32
+ DeleteCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_destroy(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+void Mutex::lock()
+{
+#ifdef _WIN32
+ EnterCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_lock(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+void Mutex::unlock()
+{
+#ifdef _WIN32
+ LeaveCriticalSection(&mutex);
+#else
+ int ret = pthread_mutex_unlock(&mutex);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+#endif
+
diff --git a/src/threading/mutex.h b/src/threading/mutex.h
new file mode 100644
index 000000000..4c9af71bf
--- /dev/null
+++ b/src/threading/mutex.h
@@ -0,0 +1,66 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_MUTEX_H
+#define THREADING_MUTEX_H
+
+// Windows std::mutex is much slower than the critical section API
+#if __cplusplus >= 201103L && !defined(_WIN32)
+ #include <mutex>
+ using Mutex = std::mutex;
+#else
+
+#ifdef _WIN32
+ #ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x0501
+ #endif
+ #ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #include <windows.h>
+#else // pthread
+ #include <pthread.h>
+#endif
+
+
+class Mutex
+{
+public:
+ Mutex();
+ ~Mutex();
+ void lock();
+ void unlock();
+
+private:
+#ifdef _WIN32
+ CRITICAL_SECTION mutex;
+#else // pthread
+ pthread_mutex_t mutex;
+#endif
+};
+
+#endif // C++11
+
+#endif
diff --git a/src/threading/mutex_auto_lock.h b/src/threading/mutex_auto_lock.h
new file mode 100644
index 000000000..1c39349e5
--- /dev/null
+++ b/src/threading/mutex_auto_lock.h
@@ -0,0 +1,50 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_MUTEX_AUTO_LOCK_H
+#define THREADING_MUTEX_AUTO_LOCK_H
+
+#if __cplusplus >= 201103L
+ #include <mutex>
+ using MutexAutoLock = std::lock_guard<std::mutex>;
+#else
+
+#include "threading/mutex.h"
+
+
+class MutexAutoLock
+{
+public:
+ MutexAutoLock(Mutex &m) : mutex(m) { mutex.lock(); }
+ ~MutexAutoLock() { mutex.unlock(); }
+
+private:
+ Mutex &mutex;
+};
+
+#endif
+
+#endif
+
diff --git a/src/threading/semaphore.cpp b/src/threading/semaphore.cpp
new file mode 100644
index 000000000..00332eaa0
--- /dev/null
+++ b/src/threading/semaphore.cpp
@@ -0,0 +1,161 @@
+/*
+Minetest
+Copyright (C) 2013 sapier <sapier AT gmx DOT net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "threading/semaphore.h"
+
+#include <iostream>
+#include <cstdlib>
+#include <cassert>
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+#ifdef _WIN32
+ #define MAX_SEMAPHORE_COUNT LONG_MAX - 1
+#else
+ #include <cerrno>
+ #include <sys/time.h>
+ #include <pthread.h>
+ #if defined(__MACH__) && defined(__APPLE__)
+ #include <mach/mach.h>
+ #include <mach/task.h>
+ #include <mach/semaphore.h>
+ #include <sys/semaphore.h>
+ #include <unistd.h>
+
+ #undef sem_t
+ #undef sem_init
+ #undef sem_wait
+ #undef sem_post
+ #undef sem_destroy
+ #define sem_t semaphore_t
+ #define sem_init(s, p, c) semaphore_create(mach_task_self(), (s), 0, (c))
+ #define sem_wait(s) semaphore_wait(*(s))
+ #define sem_post(s) semaphore_signal(*(s))
+ #define sem_destroy(s) semaphore_destroy(mach_task_self(), *(s))
+ #endif
+#endif
+
+
+Semaphore::Semaphore(int val)
+{
+#ifdef _WIN32
+ semaphore = CreateSemaphore(NULL, val, MAX_SEMAPHORE_COUNT, NULL);
+#else
+ int ret = sem_init(&semaphore, 0, val);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+
+Semaphore::~Semaphore()
+{
+#ifdef _WIN32
+ CloseHandle(semaphore);
+#else
+ int ret = sem_destroy(&semaphore);
+#ifdef __ANDROID__
+ // Workaround for broken bionic semaphore implementation!
+ assert(!ret || errno == EBUSY);
+#else
+ assert(!ret);
+#endif
+ UNUSED(ret);
+#endif
+}
+
+
+void Semaphore::post(unsigned int num)
+{
+ assert(num > 0);
+#ifdef _WIN32
+ ReleaseSemaphore(semaphore, num, NULL);
+#else
+ for (unsigned i = 0; i < num; i++) {
+ int ret = sem_post(&semaphore);
+ assert(!ret);
+ UNUSED(ret);
+ }
+#endif
+}
+
+
+void Semaphore::wait()
+{
+#ifdef _WIN32
+ WaitForSingleObject(semaphore, INFINITE);
+#else
+ int ret = sem_wait(&semaphore);
+ assert(!ret);
+ UNUSED(ret);
+#endif
+}
+
+
+bool Semaphore::wait(unsigned int time_ms)
+{
+#ifdef _WIN32
+ unsigned int ret = WaitForSingleObject(semaphore, time_ms);
+
+ if (ret == WAIT_OBJECT_0) {
+ return true;
+ } else {
+ assert(ret == WAIT_TIMEOUT);
+ return false;
+ }
+#else
+# if defined(__MACH__) && defined(__APPLE__)
+ mach_timespec_t wait_time;
+ wait_time.tv_sec = time_ms / 1000;
+ wait_time.tv_nsec = 1000000 * (time_ms % 1000);
+
+ errno = 0;
+ int ret = semaphore_timedwait(semaphore, wait_time);
+ switch (ret) {
+ case KERN_OPERATION_TIMED_OUT:
+ errno = ETIMEDOUT;
+ break;
+ case KERN_ABORTED:
+ errno = EINTR;
+ break;
+ default:
+ if (ret)
+ errno = EINVAL;
+ }
+# else
+ struct timespec wait_time;
+ struct timeval now;
+
+ if (gettimeofday(&now, NULL) == -1) {
+ std::cerr << "Semaphore::wait(ms): Unable to get time with gettimeofday!" << std::endl;
+ abort();
+ }
+
+ wait_time.tv_nsec = ((time_ms % 1000) * 1000 * 1000) + (now.tv_usec * 1000);
+ wait_time.tv_sec = (time_ms / 1000) + (wait_time.tv_nsec / (1000 * 1000 * 1000)) + now.tv_sec;
+ wait_time.tv_nsec %= 1000 * 1000 * 1000;
+
+ int ret = sem_timedwait(&semaphore, &wait_time);
+# endif
+
+ assert(!ret || (errno == ETIMEDOUT || errno == EINTR));
+ return !ret;
+#endif
+}
+
diff --git a/src/threading/semaphore.h b/src/threading/semaphore.h
new file mode 100644
index 000000000..58d758f2e
--- /dev/null
+++ b/src/threading/semaphore.h
@@ -0,0 +1,52 @@
+/*
+Minetest
+Copyright (C) 2013 sapier <sapier AT gmx DOT net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef THREADING_SEMAPHORE_H
+#define THREADING_SEMAPHORE_H
+
+#if defined(_WIN32)
+ #include <windows.h>
+#elif defined(__MACH__) && defined(__APPLE__)
+ #include <mach/semaphore.h>
+#else
+ #include <semaphore.h>
+#endif
+
+
+class Semaphore {
+public:
+ Semaphore(int val=0);
+ ~Semaphore();
+
+ void post(unsigned int num=1);
+ void wait();
+ bool wait(unsigned int time_ms);
+
+private:
+#if defined(WIN32)
+ HANDLE semaphore;
+#elif defined(__MACH__) && defined(__APPLE__)
+ semaphore_t semaphore;
+#else
+ sem_t semaphore;
+#endif
+};
+
+#endif
+
diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp
new file mode 100644
index 000000000..a1cb720af
--- /dev/null
+++ b/src/threading/thread.cpp
@@ -0,0 +1,354 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "threading/thread.h"
+#include "threading/mutex_auto_lock.h"
+#include "log.h"
+
+#if __cplusplus >= 201103L
+ #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 <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
+#endif
+
+
+// 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 dwThreadID; // Thread ID (-1=caller thread)
+ DWORD dwFlags; // Reserved for future use, must be zero
+ };
+#endif
+
+// For bindToProcessor
+#if __FreeBSD_version >= 702106
+ typedef cpuset_t cpu_set_t;
+#elif defined(__linux) || defined(linux)
+ #include <sched.h>
+#elif defined(__sun) || defined(sun)
+ #include <sys/types.h>
+ #include <sys/processor.h>
+ #include <sys/procset.h>
+#elif defined(_AIX)
+ #include <sys/processor.h>
+#elif defined(__APPLE__)
+ #include <mach/mach_init.h>
+ #include <mach/thread_policy.h>
+#endif
+
+
+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)
+#endif
+{}
+
+
+void Thread::wait()
+{
+#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
+}
+
+
+bool Thread::start()
+{
+ if (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)
+ return false;
+#else
+ int status;
+
+ MutexAutoLock l(continue_mutex);
+
+ status = pthread_create(&thread, NULL, theThread, 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
+#endif
+ return true;
+}
+
+
+bool Thread::kill()
+{
+#ifdef _WIN32
+ if (!running)
+ return false;
+ TerminateThread(getThreadHandle(), 0);
+ CloseHandle(getThreadHandle());
+#else
+ if (!running) {
+ wait();
+ return false;
+ }
+# ifdef __ANDROID__
+ pthread_kill(getThreadHandle(), SIGKILL);
+# else
+ pthread_cancel(getThreadHandle());
+# endif
+ wait();
+#endif
+#if __cplusplus >= 201103L
+ delete thread;
+#endif
+ running = false;
+ return true;
+}
+
+
+bool Thread::isSameThread()
+{
+#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);
+#endif
+}
+
+
+#if __cplusplus >= 201103L
+void Thread::theThread(Thread *th)
+#elif defined(_WIN32_WCE)
+DWORD WINAPI Thread::theThread(void *param)
+#elif defined(_WIN32)
+UINT __stdcall Thread::theThread(void *param)
+#else
+void *Thread::theThread(void *param)
+#endif
+{
+#if __cplusplus < 201103L
+ Thread *th = static_cast<Thread *>(param);
+#endif
+ th->running = true;
+
+ th->setName();
+ log_register_thread(th->name);
+
+ th->retval = th->run();
+
+ log_deregister_thread();
+
+ 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.
+ */
+ 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);
+ } __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
+}
+
+
+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(_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)
+{
+#if defined(__ANDROID__)
+ return false;
+#elif defined(_WIN32)
+ return SetThreadAffinityMask(getThreadHandle(), 1 << num);
+#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;
+#elif defined(__sun) || defined(sun)
+ return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(getThreadHandle()),
+ num, NULL) == 0
+#elif defined(_AIX)
+ return bindprocessor(BINDTHREAD, (tid_t) getThreadHandle(), pnumber) == 0;
+#elif defined(__hpux) || defined(hpux)
+ pthread_spu_t answer;
+
+ return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
+ &answer, num, getThreadHandle()) == 0;
+#elif defined(__APPLE__)
+ struct thread_affinity_policy tapol;
+
+ thread_port_t threadport = pthread_mach_thread_np(getThreadHandle());
+ tapol.affinity_tag = num + 1;
+ return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
+ (thread_policy_t)&tapol,
+ THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
+#else
+ return false;
+#endif
+}
+
+
+bool Thread::setPriority(int prio)
+{
+#if defined(_WIN32)
+ return SetThreadPriority(getThreadHandle(), prio);
+#else
+ struct sched_param sparam;
+ int policy;
+
+ if (pthread_getschedparam(getThreadHandle(), &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;
+#endif
+}
+
diff --git a/src/threading/thread.h b/src/threading/thread.h
new file mode 100644
index 000000000..275bc9b6d
--- /dev/null
+++ b/src/threading/thread.h
@@ -0,0 +1,119 @@
+/*
+This file is a part of the JThread package, which contains some object-
+oriented thread wrappers for different thread implementations.
+
+Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef THREADING_THREAD_H
+#define THREADING_THREAD_H
+
+#include "threading/atomic.h"
+#include "threading/mutex.h"
+
+#include <string>
+#if __cplusplus >= 201103L
+ #include <thread>
+#endif
+
+#ifndef _WIN32
+enum {
+ THREAD_PRIORITY_LOWEST,
+ THREAD_PRIORITY_BELOW_NORMAL,
+ THREAD_PRIORITY_NORMAL,
+ THREAD_PRIORITY_ABOVE_NORMAL,
+ THREAD_PRIORITY_HIGHEST,
+};
+#endif
+
+
+class Thread
+{
+public:
+ Thread(const std::string &name="Unnamed");
+ virtual ~Thread() { kill(); }
+
+ 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();
+
+ static unsigned int getNumberOfProcessors();
+ bool bindToProcessor(unsigned int);
+ bool setPriority(int);
+
+ /*
+ * Wait 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();
+
+ static void setName(const std::string &name);
+
+protected:
+ std::string 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
+#endif
+};
+
+#endif
+