mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-02 16:38:41 +00:00
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.
This commit is contained in:
parent
6a1047d8c1
commit
e4bff8be94
77 changed files with 1594 additions and 2046 deletions
6
src/threading/CMakeLists.txt
Normal file
6
src/threading/CMakeLists.txt
Normal file
|
@ -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)
|
||||
|
96
src/threading/atomic.h
Normal file
96
src/threading/atomic.h
Normal file
|
@ -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
|
||||
|
57
src/threading/event.h
Normal file
57
src/threading/event.h
Normal file
|
@ -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
|
||||
|
83
src/threading/mutex.cpp
Normal file
83
src/threading/mutex.cpp
Normal file
|
@ -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
|
||||
|
66
src/threading/mutex.h
Normal file
66
src/threading/mutex.h
Normal file
|
@ -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
|
50
src/threading/mutex_auto_lock.h
Normal file
50
src/threading/mutex_auto_lock.h
Normal file
|
@ -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
|
||||
|
161
src/threading/semaphore.cpp
Normal file
161
src/threading/semaphore.cpp
Normal file
|
@ -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
|
||||
}
|
||||
|
52
src/threading/semaphore.h
Normal file
52
src/threading/semaphore.h
Normal file
|
@ -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
|
||||
|
354
src/threading/thread.cpp
Normal file
354
src/threading/thread.cpp
Normal file
|
@ -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
|
||||
}
|
||||
|
119
src/threading/thread.h
Normal file
119
src/threading/thread.h
Normal file
|
@ -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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue