2018-08-11 20:27:14 -05:00
|
|
|
// SPDX-License-Identifier: AGPL-1.0-only
|
|
|
|
// Copyright (C) 2018 Ludvig Strigeus <info@tunsafe.com>. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "tunsafe_types.h"
|
|
|
|
#include <atomic>
|
|
|
|
#include <vector>
|
|
|
|
#include <assert.h>
|
|
|
|
#if !defined(OS_WIN)
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif // !defined(OS_WIN)
|
|
|
|
|
|
|
|
#if defined(OS_WIN)
|
|
|
|
|
|
|
|
class ReaderWriterLock {
|
|
|
|
public:
|
|
|
|
ReaderWriterLock() : lock_(SRWLOCK_INIT) {}
|
|
|
|
void AcquireExclusive() { AcquireSRWLockExclusive(&lock_); }
|
|
|
|
void AcquireShared() { AcquireSRWLockShared(&lock_); }
|
|
|
|
void ReleaseExclusive() { ReleaseSRWLockExclusive(&lock_); }
|
|
|
|
void ReleaseShared() { ReleaseSRWLockShared(&lock_); }
|
|
|
|
private:
|
|
|
|
SRWLOCK lock_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Mutex {
|
2018-10-07 12:36:52 -05:00
|
|
|
friend class ConditionVariable;
|
2018-08-11 20:27:14 -05:00
|
|
|
public:
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
bool locked_;
|
|
|
|
bool IsLocked() { return locked_; }
|
|
|
|
#define Mutex_SETLOCKED(x) locked_ = x;
|
|
|
|
#else
|
|
|
|
bool IsLocked() { return false; }
|
|
|
|
#define Mutex_SETLOCKED(x)
|
|
|
|
#endif
|
|
|
|
Mutex() : lock_(SRWLOCK_INIT) { Mutex_SETLOCKED(false); }
|
|
|
|
~Mutex() { }
|
|
|
|
void Acquire() {
|
|
|
|
AcquireSRWLockExclusive(&lock_);
|
|
|
|
Mutex_SETLOCKED(true);
|
|
|
|
}
|
|
|
|
void Release() {
|
|
|
|
Mutex_SETLOCKED(false);
|
|
|
|
ReleaseSRWLockExclusive(&lock_);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
SRWLOCK lock_;
|
|
|
|
};
|
|
|
|
|
2018-10-07 12:36:52 -05:00
|
|
|
class ConditionVariable {
|
|
|
|
public:
|
|
|
|
ConditionVariable() { InitializeConditionVariable(&condvar_); }
|
|
|
|
void Wait(Mutex *mutex) { SleepConditionVariableSRW(&condvar_, &mutex->lock_, INFINITE, 0); }
|
|
|
|
void WaitTimed(Mutex *mutex, int millis) { SleepConditionVariableSRW(&condvar_, &mutex->lock_, millis, 0); }
|
|
|
|
void Wake() { WakeConditionVariable(&condvar_); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
CONDITION_VARIABLE condvar_;
|
|
|
|
};
|
|
|
|
|
2018-08-11 20:27:14 -05:00
|
|
|
typedef uint32 ThreadId;
|
|
|
|
|
|
|
|
static inline bool CurrentThreadIdEquals(ThreadId thread_id) {
|
|
|
|
return thread_id == GetCurrentThreadId();
|
|
|
|
}
|
|
|
|
|
|
|
|
#else // defined(OS_WIN)
|
|
|
|
|
|
|
|
class ReaderWriterLock {
|
|
|
|
public:
|
|
|
|
ReaderWriterLock() {
|
|
|
|
if (pthread_rwlock_init(&lock_, NULL) != 0)
|
|
|
|
tunsafe_die("pthread_rwlock_init failed");
|
|
|
|
}
|
|
|
|
~ReaderWriterLock() {
|
|
|
|
pthread_rwlock_destroy(&lock_);
|
|
|
|
}
|
|
|
|
void AcquireExclusive() { int rv = pthread_rwlock_wrlock(&lock_); assert(rv == 0); }
|
|
|
|
void AcquireShared() { int rv = pthread_rwlock_rdlock(&lock_); assert(rv == 0); }
|
|
|
|
void ReleaseExclusive() { int rv = pthread_rwlock_unlock(&lock_); assert(rv == 0); }
|
|
|
|
void ReleaseShared() { int rv = pthread_rwlock_unlock(&lock_); assert(rv == 0); }
|
|
|
|
private:
|
|
|
|
pthread_rwlock_t lock_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Mutex {
|
2018-10-07 12:36:52 -05:00
|
|
|
friend class ConditionVariable;
|
2018-08-11 20:27:14 -05:00
|
|
|
public:
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
bool locked_;
|
|
|
|
bool IsLocked() { return locked_; }
|
|
|
|
#define Mutex_SETLOCKED(x) locked_ = x;
|
|
|
|
#else
|
|
|
|
bool IsLocked() { return false; }
|
|
|
|
#define Mutex_SETLOCKED(x)
|
|
|
|
#endif
|
|
|
|
Mutex() {
|
|
|
|
if (pthread_mutex_init(&lock_, NULL) != 0)
|
|
|
|
tunsafe_die("pthread_mutex_init failed");
|
|
|
|
Mutex_SETLOCKED(false);
|
|
|
|
}
|
|
|
|
~Mutex() {
|
|
|
|
pthread_mutex_destroy(&lock_);
|
|
|
|
}
|
|
|
|
void Acquire() {
|
|
|
|
int rv = pthread_mutex_lock(&lock_);
|
|
|
|
assert(rv == 0);
|
|
|
|
Mutex_SETLOCKED(true);
|
|
|
|
}
|
|
|
|
void Release() {
|
|
|
|
Mutex_SETLOCKED(false);
|
|
|
|
int rv = pthread_mutex_unlock(&lock_);
|
|
|
|
assert(rv == 0);
|
|
|
|
}
|
|
|
|
pthread_mutex_t *impl() { return &lock_; }
|
|
|
|
private:
|
|
|
|
pthread_mutex_t lock_;
|
|
|
|
};
|
|
|
|
|
2018-10-07 12:36:52 -05:00
|
|
|
class ConditionVariable {
|
|
|
|
public:
|
|
|
|
ConditionVariable() { pthread_cond_init(&condvar_, NULL); }
|
|
|
|
void Wait(Mutex *mutex) { pthread_cond_wait(&condvar_, &mutex->lock_); }
|
|
|
|
void WaitTimed(Mutex *mutex, int millis);
|
|
|
|
void Wake() { pthread_cond_signal(&condvar_); }
|
|
|
|
private:
|
|
|
|
pthread_cond_t condvar_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-08-11 20:27:14 -05:00
|
|
|
typedef pthread_t ThreadId;
|
|
|
|
|
|
|
|
static inline bool CurrentThreadIdEquals(ThreadId thread_id) {
|
|
|
|
return pthread_equal(thread_id, pthread_self()) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ThreadId GetCurrentThreadId() {
|
|
|
|
return pthread_self();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // !defined(OS_WIN)
|
|
|
|
|
|
|
|
class ScopedLockShared {
|
|
|
|
public:
|
|
|
|
ScopedLockShared(ReaderWriterLock *lock) : lock_(lock) { lock->AcquireShared(); }
|
|
|
|
~ScopedLockShared() { lock_->ReleaseShared(); }
|
|
|
|
private:
|
|
|
|
ReaderWriterLock *lock_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ScopedLockExclusive {
|
|
|
|
public:
|
|
|
|
ScopedLockExclusive(ReaderWriterLock *lock) : lock_(lock) { lock->AcquireExclusive(); }
|
|
|
|
~ScopedLockExclusive() { lock_->ReleaseExclusive(); }
|
|
|
|
private:
|
|
|
|
ReaderWriterLock *lock_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class ScopedLock {
|
|
|
|
public:
|
|
|
|
ScopedLock(Mutex *lock) : lock_(lock) { lock->Acquire(); }
|
|
|
|
~ScopedLock() { lock_->Release(); }
|
|
|
|
private:
|
|
|
|
Mutex *lock_;
|
|
|
|
};
|
|
|
|
|
2018-10-07 12:36:52 -05:00
|
|
|
class Thread {
|
|
|
|
public:
|
|
|
|
class Runner {
|
|
|
|
public:
|
|
|
|
virtual void ThreadMain() = 0;
|
|
|
|
};
|
|
|
|
Thread();
|
|
|
|
~Thread();
|
|
|
|
void StartThread(Runner *runner);
|
|
|
|
void StopThread();
|
|
|
|
void DetachThread();
|
|
|
|
bool is_started();
|
|
|
|
|
|
|
|
private:
|
|
|
|
#if defined(OS_WIN)
|
|
|
|
HANDLE thread_;
|
|
|
|
#else // defined(OS_WIN)
|
|
|
|
pthread_t thread_;
|
|
|
|
#endif // !defined(OS_WIN)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-08-11 20:27:14 -05:00
|
|
|
// This class deletes objects delayed. All participating threads will call a function,
|
|
|
|
// and then once all threads did, all registered objects will get deleted.
|
|
|
|
class MultithreadedDelayedDelete {
|
|
|
|
public:
|
|
|
|
MultithreadedDelayedDelete();
|
|
|
|
~MultithreadedDelayedDelete();
|
|
|
|
|
|
|
|
typedef void DoDeleteFunc(void *x);
|
|
|
|
void Add(DoDeleteFunc *func, void *param);
|
|
|
|
|
2018-09-15 11:22:05 -05:00
|
|
|
void Configure(uint32 num_threads);
|
2018-08-11 20:27:14 -05:00
|
|
|
|
|
|
|
void Checkpoint(uint32 thread_id);
|
|
|
|
|
|
|
|
void MainCheckpoint();
|
|
|
|
|
2018-09-15 11:22:05 -05:00
|
|
|
bool enabled() const { return num_threads_ != 0; }
|
|
|
|
|
2018-08-11 20:27:14 -05:00
|
|
|
private:
|
|
|
|
struct Entry {
|
|
|
|
DoDeleteFunc *func;
|
|
|
|
void *param;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CheckpointData {
|
|
|
|
std::atomic<uint32> value;
|
|
|
|
uint8 align[60];
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32 num_threads_;
|
|
|
|
|
|
|
|
std::vector<Entry> curr_, next_, to_delete_;
|
|
|
|
CheckpointData *table_;
|
|
|
|
Mutex lock_;
|
|
|
|
};
|