1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-07-22 17:18:39 +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:
ShadowNinja 2015-04-07 06:13:12 -04:00
parent 6a1047d8c1
commit e4bff8be94
77 changed files with 1594 additions and 2046 deletions

View file

@ -22,9 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../irrlichttypes.h"
#include "../exceptions.h"
#include "../jthread/jmutex.h"
#include "../jthread/jmutexautolock.h"
#include "../jthread/jsemaphore.h"
#include "../threading/mutex.h"
#include "../threading/mutex_auto_lock.h"
#include "../threading/semaphore.h"
#include <list>
#include <vector>
#include <map>
@ -81,111 +81,47 @@ template<typename Key, typename Value>
class MutexedMap
{
public:
MutexedMap()
{
}
MutexedMap() {}
void set(const Key &name, const Value &value)
{
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
m_values[name] = value;
}
bool get(const Key &name, Value *result)
bool get(const Key &name, Value *result) const
{
JMutexAutoLock lock(m_mutex);
typename std::map<Key, Value>::iterator n;
n = m_values.find(name);
if(n == m_values.end())
MutexAutoLock lock(m_mutex);
typename std::map<Key, Value>::const_iterator n =
m_values.find(name);
if (n == m_values.end())
return false;
if(result != NULL)
if (result)
*result = n->second;
return true;
}
std::vector<Value> getValues()
std::vector<Value> getValues() const
{
MutexAutoLock lock(m_mutex);
std::vector<Value> result;
for(typename std::map<Key, Value>::iterator
i = m_values.begin();
i != m_values.end(); ++i){
result.push_back(i->second);
for (typename std::map<Key, Value>::const_iterator
it = m_values.begin();
it != m_values.end(); ++it){
result.push_back(it->second);
}
return result;
}
void clear ()
{
m_values.clear();
}
void clear() { m_values.clear(); }
private:
std::map<Key, Value> m_values;
JMutex m_mutex;
mutable Mutex m_mutex;
};
/*
Generates ids for comparable values.
Id=0 is reserved for "no value".
Is fast at:
- Returning value by id (very fast)
- Returning id by value
- Generating a new id for a value
Is not able to:
- Remove an id/value pair (is possible to implement but slow)
*/
template<typename T>
class MutexedIdGenerator
{
public:
MutexedIdGenerator()
{
}
// Returns true if found
bool getValue(u32 id, T &value)
{
if(id == 0)
return false;
JMutexAutoLock lock(m_mutex);
if(m_id_to_value.size() < id)
return false;
value = m_id_to_value[id-1];
return true;
}
// If id exists for value, returns the id.
// Otherwise generates an id for the value.
u32 getId(const T &value)
{
JMutexAutoLock lock(m_mutex);
typename std::map<T, u32>::iterator n;
n = m_value_to_id.find(value);
if(n != m_value_to_id.end())
return n->second;
m_id_to_value.push_back(value);
u32 new_id = m_id_to_value.size();
m_value_to_id.insert(value, new_id);
return new_id;
}
private:
JMutex m_mutex;
// Values are stored here at id-1 position (id 1 = [0])
std::vector<T> m_id_to_value;
std::map<T, u32> m_value_to_id;
};
/*
Thread-safe FIFO queue (well, actually a FILO also)
*/
// Thread-safe Double-ended queue
template<typename T>
class MutexedQueue
@ -194,19 +130,18 @@ public:
template<typename Key, typename U, typename Caller, typename CallerData>
friend class RequestQueue;
MutexedQueue()
MutexedQueue() {}
bool empty() const
{
MutexAutoLock lock(m_mutex);
return m_queue.empty();
}
bool empty()
{
JMutexAutoLock lock(m_mutex);
return (m_queue.size() == 0);
}
void push_back(T t)
{
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
m_queue.push_back(t);
m_size.Post();
m_signal.post();
}
/* this version of pop_front returns a empty element of T on timeout.
@ -214,37 +149,35 @@ public:
*/
T pop_frontNoEx(u32 wait_time_max_ms)
{
if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
return t;
}
else {
} else {
return T();
}
}
T pop_front(u32 wait_time_max_ms)
{
if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
return t;
}
else {
} else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
T pop_frontNoEx()
{
m_size.Wait();
m_signal.wait();
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
T t = m_queue.front();
m_queue.pop_front();
@ -253,14 +186,13 @@ public:
T pop_back(u32 wait_time_max_ms=0)
{
if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
return t;
}
else {
} else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
@ -268,25 +200,24 @@ public:
/* this version of pop_back returns a empty element of T on timeout.
* Make sure default constructor of T creates a recognizable "empty" element
*/
T pop_backNoEx(u32 wait_time_max_ms=0)
T pop_backNoEx(u32 wait_time_max_ms)
{
if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
if (m_signal.wait(wait_time_max_ms)) {
MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
return t;
}
else {
} else {
return T();
}
}
T pop_backNoEx()
{
m_size.Wait();
m_signal.wait();
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
T t = m_queue.back();
m_queue.pop_back();
@ -294,19 +225,13 @@ public:
}
protected:
JMutex & getMutex()
{
return m_mutex;
}
Mutex &getMutex() { return m_mutex; }
std::deque<T> & getQueue()
{
return m_queue;
}
std::deque<T> &getQueue() { return m_queue; }
std::deque<T> m_queue;
JMutex m_mutex;
JSemaphore m_size;
mutable Mutex m_mutex;
Semaphore m_signal;
};
template<typename K, typename V>

View file

@ -23,17 +23,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "../constants.h" // BS, MAP_BLOCKSIZE
#include "../noise.h" // PseudoRandom, PcgRandom
#include "../jthread/jmutexautolock.h"
#include "../threading/mutex_auto_lock.h"
#include <string.h>
#include <iostream>
std::map<u16, std::vector<v3s16> > FacePositionCache::m_cache;
JMutex FacePositionCache::m_cache_mutex;
Mutex FacePositionCache::m_cache_mutex;
// Calculate the borders of a "d-radius" cube
// TODO: Make it work without mutex and data races, probably thread-local
std::vector<v3s16> FacePositionCache::getFacePositions(u16 d)
{
JMutexAutoLock cachelock(m_cache_mutex);
MutexAutoLock cachelock(m_cache_mutex);
if (m_cache.find(d) != m_cache.end())
return m_cache[d];

View file

@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../irr_v2d.h"
#include "../irr_v3d.h"
#include "../irr_aabb3d.h"
#include "../jthread/jmutex.h"
#include "../threading/mutex.h"
#include <list>
#include <map>
#include <vector>
@ -42,7 +42,7 @@ public:
private:
static void generateFacePosition(u16 d);
static std::map<u16, std::vector<v3s16> > m_cache;
static JMutex m_cache_mutex;
static Mutex m_cache_mutex;
};
class IndentationRaiser

View file

@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_THREAD_HEADER
#include "../irrlichttypes.h"
#include "../jthread/jthread.h"
#include "../jthread/jmutex.h"
#include "../jthread/jmutexautolock.h"
#include "../threading/thread.h"
#include "../threading/mutex.h"
#include "../threading/mutex_auto_lock.h"
#include "porting.h"
#include "log.h"
@ -36,27 +36,27 @@ public:
T get()
{
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
return m_value;
}
void set(T value)
{
JMutexAutoLock lock(m_mutex);
MutexAutoLock lock(m_mutex);
m_value = value;
}
// You'll want to grab this in a SharedPtr
JMutexAutoLock *getLock()
MutexAutoLock *getLock()
{
return new JMutexAutoLock(m_mutex);
return new MutexAutoLock(m_mutex);
}
// You pretty surely want to grab the lock when accessing this
T m_value;
private:
JMutex m_mutex;
Mutex m_mutex;
};
/*
@ -118,7 +118,7 @@ public:
typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
{
JMutexAutoLock lock(m_queue.getMutex());
MutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
@ -192,44 +192,33 @@ private:
MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
};
class UpdateThread : public JThread {
class UpdateThread : public Thread
{
public:
UpdateThread() {}
virtual ~UpdateThread() {}
UpdateThread(const std::string &name) : Thread(name + "Update") {}
~UpdateThread() {}
void deferUpdate()
{
m_update_sem.Post();
}
void deferUpdate() { m_update_sem.post(); }
void Stop()
void stop()
{
JThread::Stop();
Thread::stop();
// give us a nudge
m_update_sem.Post();
m_update_sem.post();
}
void *Thread()
void *run()
{
ThreadStarted();
const char *thread_name = getName();
log_register_thread(thread_name);
porting::setThreadName(thread_name);
DSTACK(__FUNCTION_NAME);
BEGIN_DEBUG_EXCEPTION_HANDLER
while (!StopRequested()) {
m_update_sem.Wait();
while (!stopRequested()) {
m_update_sem.wait();
// Set semaphore to 0
while (m_update_sem.wait(0));
// Empty the queue, just in case doUpdate() is expensive
while (m_update_sem.GetValue())
m_update_sem.Wait();
if (StopRequested())
break;
if (stopRequested()) break;
doUpdate();
}
@ -241,10 +230,9 @@ public:
protected:
virtual void doUpdate() = 0;
virtual const char *getName() = 0;
private:
JSemaphore m_update_sem;
Semaphore m_update_sem;
};
#endif