lokinet/llarp/util/thread/threading.hpp

183 lines
4.1 KiB
C++
Raw Normal View History

#ifndef LLARP_THREADING_HPP
#define LLARP_THREADING_HPP
#include <absl/synchronization/barrier.h>
#include <absl/synchronization/mutex.h>
#include <absl/types/optional.h>
#include <absl/time/time.h>
2018-08-12 17:22:29 +00:00
#include <iostream>
#include <thread>
2019-07-22 22:20:17 +00:00
#if defined(WIN32) && !defined(__GNUC__)
2019-07-17 09:23:46 +00:00
#include <process.h>
using pid_t = int;
#else
#include <sys/types.h>
#include <unistd.h>
#endif
#ifdef TRACY_ENABLE
#include "Tracy.hpp"
#define DECLARE_LOCK(type, var, ...) TracyLockable(type, var)
#define ACQUIRE_LOCK(lock, mtx) lock(mtx)
#else
#define DECLARE_LOCK(type, var, ...) type var __VA_ARGS__
#define ACQUIRE_LOCK(lock, mtx) lock(&mtx)
#endif
2018-08-12 17:22:29 +00:00
namespace llarp
{
namespace util
{
/// a mutex that does nothing
///
/// this exists to convert mutexes that were initially in use (but may no
/// longer be necessary) into no-op placeholders (except in debug mode
/// where they complain loudly when they are actually accessed across
/// different threads; see below).
///
/// the idea is to "turn off" the mutexes and see where they are actually
/// needed.
struct LOCKABLE NullMutex
2018-08-12 17:22:29 +00:00
{
#ifdef LOKINET_DEBUG
/// in debug mode, we implement lock() to enforce that any lock is only
/// used from a single thread. the point of this is to identify locks that
/// are actually needed by dying a painful death when used across threads
mutable absl::optional< std::thread::id > m_id;
void
lock() const
{
if(!m_id)
{
m_id.emplace(std::this_thread::get_id());
}
else if(m_id.value() != std::this_thread::get_id())
{
std::cerr << "NullMutex " << this
<< " was used across threads: locked by "
<< std::this_thread::get_id()
<< " and was previously locked by " << m_id.value() << "\n";
// if you're encountering this abort() call, you may have discovered a
// case where a NullMutex should be reverted to a "real mutex"
std::abort();
}
}
#else
void
lock() const
{
}
#endif
2018-08-12 17:22:29 +00:00
};
/// a lock that does nothing
struct SCOPED_LOCKABLE NullLock
2018-08-12 17:22:29 +00:00
{
2019-12-07 12:28:43 +00:00
NullLock(const NullMutex* mtx) EXCLUSIVE_LOCK_FUNCTION(mtx)
{
mtx->lock();
}
~NullLock() UNLOCK_FUNCTION()
2018-08-12 17:22:29 +00:00
{
2019-07-30 23:42:13 +00:00
(void)this; // trick clang-tidy
2018-08-12 17:22:29 +00:00
}
};
using Mutex = absl::Mutex;
using Lock = absl::MutexLock;
using ReleasableLock = absl::ReleasableMutexLock;
using Condition = absl::CondVar;
2018-08-12 17:22:29 +00:00
2018-11-17 21:07:04 +00:00
class Semaphore
{
private:
Mutex m_mutex; // protects m_count
size_t m_count GUARDED_BY(m_mutex);
bool
ready() const SHARED_LOCKS_REQUIRED(m_mutex)
{
return m_count > 0;
}
2018-11-17 21:07:04 +00:00
public:
Semaphore(size_t count) : m_count(count)
{
}
void
notify() LOCKS_EXCLUDED(m_mutex)
2018-11-17 21:07:04 +00:00
{
Lock lock(&m_mutex);
2018-11-17 21:07:04 +00:00
m_count++;
}
void
wait() LOCKS_EXCLUDED(m_mutex)
2018-11-17 21:07:04 +00:00
{
Lock lock(&m_mutex);
m_mutex.Await(absl::Condition(this, &Semaphore::ready));
2018-11-17 21:07:04 +00:00
m_count--;
}
bool
waitFor(absl::Duration timeout) LOCKS_EXCLUDED(m_mutex)
2018-11-17 21:07:04 +00:00
{
Lock lock(&m_mutex);
2018-11-17 21:07:04 +00:00
if(!m_mutex.AwaitWithTimeout(absl::Condition(this, &Semaphore::ready),
timeout))
2018-11-17 21:07:04 +00:00
{
return false;
2018-11-17 21:07:04 +00:00
}
m_count--;
return true;
2018-11-17 21:07:04 +00:00
}
};
using Barrier = absl::Barrier;
void
SetThreadName(const std::string& name);
2019-07-17 09:23:46 +00:00
inline pid_t
GetPid()
{
#ifdef WIN32
return _getpid();
#else
return ::getpid();
#endif
2019-07-17 09:23:46 +00:00
}
2019-11-14 18:50:45 +00:00
// type for detecting contention on a resource
struct ContentionKiller
{
2019-11-19 19:16:22 +00:00
template < typename F >
2019-11-14 18:50:45 +00:00
void
2019-12-07 19:22:10 +00:00
TryAccess(F visit) const
#if defined(LOKINET_DEBUG)
LOCKS_EXCLUDED(_access)
#endif
2019-11-19 19:16:22 +00:00
{
#if defined(LOKINET_DEBUG)
2019-12-07 12:28:43 +00:00
NullLock lock(&_access);
#endif
2019-11-19 19:16:22 +00:00
visit();
}
2019-12-10 14:14:16 +00:00
#if defined(LOKINET_DEBUG)
2019-11-14 18:50:45 +00:00
private:
2019-12-07 12:28:43 +00:00
mutable NullMutex _access;
2019-12-10 14:14:16 +00:00
#endif
2019-11-14 18:50:45 +00:00
};
2018-08-12 17:22:29 +00:00
} // namespace util
} // namespace llarp
2018-08-16 14:34:15 +00:00
#endif