521 lines
11 KiB
C++
521 lines
11 KiB
C++
|
#include "sled/system/thread.h"
|
||
|
#include "sled/cleanup.h"
|
||
|
#include "sled/network/null_socket_server.h"
|
||
|
#include "sled/network/socket_server.h"
|
||
|
#include "sled/synchronization/event.h"
|
||
|
#include "sled/synchronization/thread_local.h"
|
||
|
#include "sled/time_utils.h"
|
||
|
#include <atomic>
|
||
|
#include <memory>
|
||
|
#include <thread>
|
||
|
|
||
|
namespace sled {
|
||
|
|
||
|
ThreadManager *
|
||
|
ThreadManager::Instance()
|
||
|
{
|
||
|
static ThreadManager *const thread_manager = new ThreadManager();
|
||
|
return thread_manager;
|
||
|
}
|
||
|
|
||
|
ThreadManager::ThreadManager() { current_thread_.Set(nullptr); }
|
||
|
|
||
|
ThreadManager::~ThreadManager() {}
|
||
|
|
||
|
void
|
||
|
ThreadManager::Add(Thread *message_queue)
|
||
|
{
|
||
|
return Instance()->AddInternal(message_queue);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::AddInternal(Thread *message_queue)
|
||
|
{
|
||
|
MutexLock lock(&cirt_);
|
||
|
message_queues_.push_back(message_queue);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::Remove(Thread *message_queue)
|
||
|
{
|
||
|
return Instance()->RemoveInternal(message_queue);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::RemoveInternal(Thread *message_queue)
|
||
|
{
|
||
|
MutexLock lock(&cirt_);
|
||
|
auto iter = std::find(message_queues_.begin(), message_queues_.end(),
|
||
|
message_queue);
|
||
|
if (iter != message_queues_.end()) { message_queues_.erase(iter); }
|
||
|
}
|
||
|
|
||
|
Thread *
|
||
|
ThreadManager::CurrentThread()
|
||
|
{
|
||
|
return current_thread_.Get();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::SetCurrentThread(Thread *thread)
|
||
|
{
|
||
|
if (thread) {
|
||
|
thread->EnsureIsCurrentTaskQueue();
|
||
|
} else {
|
||
|
Thread *current = CurrentThread();
|
||
|
if (current) { current->ClearCurrentTaskQueue(); }
|
||
|
}
|
||
|
Instance()->SetCurrentThreadInternal(thread);
|
||
|
}
|
||
|
|
||
|
Thread *
|
||
|
ThreadManager::WrapCurrentThread()
|
||
|
{
|
||
|
Thread *result = CurrentThread();
|
||
|
if (result == nullptr) {
|
||
|
result = new Thread(CreateDefaultSocketServer());
|
||
|
result->WrapCurrentWithThreadManager(this, true);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::UnwrapCurrentThread()
|
||
|
{
|
||
|
Thread *t = CurrentThread();
|
||
|
if (t && !(t->IsOwned())) {
|
||
|
t->UnwrapCurrent();
|
||
|
delete t;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::ProcessAllMessageQueueInternal()
|
||
|
{
|
||
|
std::atomic<int> queues_not_done(0);
|
||
|
MutexLock lock(&cirt_);
|
||
|
for (Thread *queue : message_queues_) {
|
||
|
queues_not_done.fetch_add(1);
|
||
|
auto sub =
|
||
|
MakeCleanup([&queues_not_done] { queues_not_done.fetch_sub(1); });
|
||
|
queue->PostDelayedTask([&sub] {}, TimeDelta::Zero());
|
||
|
}
|
||
|
|
||
|
Thread *current = Thread::Current();
|
||
|
while (queues_not_done.load() > 0) {
|
||
|
if (current) { current->ProcessMessages(0); }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ThreadManager::SetCurrentThreadInternal(Thread *message_queue)
|
||
|
{
|
||
|
current_thread_.Set(message_queue);
|
||
|
}
|
||
|
|
||
|
Thread::Thread(SocketServer *ss) : Thread(ss, /*do_init=*/true) {}
|
||
|
|
||
|
Thread::Thread(std::unique_ptr<SocketServer> ss)
|
||
|
: Thread(std::move(ss), /*do_init=*/true)
|
||
|
{}
|
||
|
|
||
|
Thread::Thread(SocketServer *ss, bool do_init)
|
||
|
: delayed_next_num_(0),
|
||
|
fInitialized_(false),
|
||
|
fDestroyed_(false),
|
||
|
stop_(0),
|
||
|
ss_(ss)
|
||
|
{
|
||
|
ss_->SetMessageQueue(this);
|
||
|
SetName("Thread", this);
|
||
|
if (do_init) { DoInit(); }
|
||
|
}
|
||
|
|
||
|
Thread::Thread(std::unique_ptr<SocketServer> ss, bool do_init)
|
||
|
: Thread(ss.get(), do_init)
|
||
|
{
|
||
|
own_ss_ = std::move(ss);
|
||
|
}
|
||
|
|
||
|
Thread::~Thread()
|
||
|
{
|
||
|
Stop();
|
||
|
DoDestroy();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::DoInit()
|
||
|
{
|
||
|
if (fInitialized_) { return; }
|
||
|
|
||
|
fInitialized_ = true;
|
||
|
ThreadManager::Add(this);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::DoDestroy()
|
||
|
{
|
||
|
if (fDestroyed_) { return; }
|
||
|
fDestroyed_ = true;
|
||
|
if (ss_) { ss_->SetMessageQueue(nullptr); }
|
||
|
CurrentTaskQueueSetter set_current(this);
|
||
|
messages_ = {};
|
||
|
delayed_messages_ = {};
|
||
|
}
|
||
|
|
||
|
SocketServer *
|
||
|
Thread::socketserver()
|
||
|
{
|
||
|
return ss_;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::WakeUpSocketServer()
|
||
|
{
|
||
|
ss_->WakeUp();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Quit()
|
||
|
{
|
||
|
stop_.store(1, std::memory_order_release);
|
||
|
WakeUpSocketServer();
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::IsQuitting()
|
||
|
{
|
||
|
return stop_.load(std::memory_order_acquire) != 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Restart()
|
||
|
{
|
||
|
stop_.store(0, std::memory_order_release);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Stop()
|
||
|
{
|
||
|
Thread::Quit();
|
||
|
Join();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Run()
|
||
|
{
|
||
|
ProcessMessages(kForever);
|
||
|
}
|
||
|
|
||
|
std::function<void()>
|
||
|
Thread::Get(int cmsWait)
|
||
|
{
|
||
|
int64_t cmsTotal = cmsWait;
|
||
|
int64_t cmsElapsed = 0;
|
||
|
int64_t msStart = TimeMillis();
|
||
|
int64_t msCurrent = msStart;
|
||
|
|
||
|
while (true) {
|
||
|
int64_t cmsDelayNext = kForever;
|
||
|
{
|
||
|
MutexLock lock(&mutex_);
|
||
|
// check delayed_messages_
|
||
|
while (!delayed_messages_.empty()) {
|
||
|
int64_t first_run_time_ms = delayed_messages_.top().run_time_ms;
|
||
|
if (msCurrent < first_run_time_ms) {
|
||
|
cmsDelayNext = TimeDiff(first_run_time_ms, msCurrent);
|
||
|
break;
|
||
|
}
|
||
|
messages_.push(std::move(delayed_messages_.top().functor));
|
||
|
delayed_messages_.pop();
|
||
|
}
|
||
|
// check messages_
|
||
|
if (!messages_.empty()) {
|
||
|
std::function<void()> task = std::move(messages_.front());
|
||
|
messages_.pop();
|
||
|
return std::move(task);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IsQuitting()) { break; }
|
||
|
|
||
|
int64_t cmsNext;
|
||
|
if (cmsWait == kForever) {
|
||
|
cmsNext = cmsDelayNext;
|
||
|
} else {
|
||
|
cmsNext = std::max<int64_t>(0, cmsTotal - cmsElapsed);
|
||
|
if ((cmsDelayNext != kForever) && (cmsDelayNext < cmsNext)) {
|
||
|
cmsNext = cmsDelayNext;
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
if (!ss_->Wait(cmsNext == kForever ? SocketServer::kForever
|
||
|
: TimeDelta::Millis(cmsNext),
|
||
|
/*process_io=*/true)) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
msCurrent = TimeMillis();
|
||
|
cmsElapsed = TimeDiff(msCurrent, msStart);
|
||
|
if (cmsWait != kForever) {
|
||
|
if (cmsElapsed >= cmsWait) { return nullptr; }
|
||
|
}
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::PostTaskImpl(std::function<void()> &&task,
|
||
|
const PostTaskTraits &traits,
|
||
|
const Location &location)
|
||
|
{
|
||
|
if (IsQuitting()) { return; }
|
||
|
{
|
||
|
MutexLock lock(&mutex_);
|
||
|
messages_.push(std::move(task));
|
||
|
}
|
||
|
WakeUpSocketServer();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::PostDelayedTaskImpl(std::function<void()> &&task,
|
||
|
TimeDelta delay,
|
||
|
const PostDelayedTaskTraits &traits,
|
||
|
const Location &location)
|
||
|
{
|
||
|
if (IsQuitting()) { return; }
|
||
|
|
||
|
int64_t delay_ms = delay.RoundUpTo(TimeDelta::Millis(1)).ms<int>();
|
||
|
int64_t run_time_ms = TimeAfterMillis(delay_ms);
|
||
|
{
|
||
|
MutexLock lock(&mutex_);
|
||
|
delayed_messages_.push({.delay_ms = delay_ms,
|
||
|
.run_time_ms = run_time_ms,
|
||
|
.message_number = delayed_next_num_,
|
||
|
.functor = std::move(task)
|
||
|
|
||
|
});
|
||
|
++delayed_next_num_;
|
||
|
// assert delayed_next_num_ != 0
|
||
|
}
|
||
|
WakeUpSocketServer();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::BlockingCallImpl(std::function<void()> functor,
|
||
|
const Location &location)
|
||
|
{
|
||
|
if (IsQuitting()) { return; }
|
||
|
if (IsCurrent()) {
|
||
|
functor();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Thread *current_thread = Thread::Current();
|
||
|
Event done;
|
||
|
PostTask([functor, &done] {
|
||
|
functor();
|
||
|
done.Set();
|
||
|
});
|
||
|
done.Wait(Event::kForever);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
Thread::GetDelay()
|
||
|
{
|
||
|
MutexLock lock(&mutex_);
|
||
|
if (!messages_.empty()) { return 0; }
|
||
|
|
||
|
if (!delayed_messages_.empty()) {
|
||
|
int delay = TimeUntilMillis(delayed_messages_.top().run_time_ms);
|
||
|
return std::max<int>(0, delay);
|
||
|
}
|
||
|
|
||
|
return kForever;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Dispatch(std::function<void()> &&task)
|
||
|
{
|
||
|
int64_t start_time = TimeMillis();
|
||
|
std::move(task)();
|
||
|
int64_t end_time = TimeMillis();
|
||
|
int64_t diff = TimeDiff(end_time, start_time);
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::SleepMs(int milliseconds)
|
||
|
{
|
||
|
struct timespec ts;
|
||
|
ts.tv_sec = milliseconds / 1000;
|
||
|
ts.tv_nsec = (milliseconds % 1000) * 1000000;
|
||
|
int ret = nanosleep(&ts, nullptr);
|
||
|
if (ret != 0) {
|
||
|
// error log
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::SetName(const std::string &name, const void *obj)
|
||
|
{
|
||
|
name_ = name;
|
||
|
if (obj) {
|
||
|
char buf[30];
|
||
|
snprintf(buf, sizeof(buf), " 0x%p", obj);
|
||
|
name_ += buf;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::EnsureIsCurrentTaskQueue()
|
||
|
{
|
||
|
task_queue_registration_.reset(
|
||
|
new TaskQueueBase::CurrentTaskQueueSetter(this));
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::ClearCurrentTaskQueue()
|
||
|
{
|
||
|
task_queue_registration_.reset();
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::Start()
|
||
|
{
|
||
|
if (IsRunning()) { return false; }
|
||
|
|
||
|
Restart();
|
||
|
ThreadManager::Instance();
|
||
|
owned_ = true;
|
||
|
thread_.reset(new std::thread(&Thread::PreRun, this));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Join()
|
||
|
{
|
||
|
if (!IsRunning()) { return; }
|
||
|
thread_->join();
|
||
|
thread_.reset();
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::IsCurrent() const
|
||
|
{
|
||
|
return ThreadManager::Instance()->CurrentThread() == this;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::IsRunning()
|
||
|
{
|
||
|
return thread_ != nullptr;
|
||
|
}
|
||
|
|
||
|
void *
|
||
|
Thread::PreRun(void *pv)
|
||
|
{
|
||
|
Thread *thread = static_cast<Thread *>(pv);
|
||
|
ThreadManager::Instance()->SetCurrentThread(thread);
|
||
|
thread->Run();
|
||
|
ThreadManager::Instance()->SetCurrentThread(nullptr);
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::WrapCurrentWithThreadManager(ThreadManager *thread_manager,
|
||
|
bool need_synchronize_access)
|
||
|
{
|
||
|
// assert(!IsRunning());
|
||
|
owned_ = false;
|
||
|
thread_manager->SetCurrentThread(this);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::IsOwned() const
|
||
|
{
|
||
|
return owned_;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::WrapCurrent()
|
||
|
{
|
||
|
return WrapCurrentWithThreadManager(ThreadManager::Instance(), true);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::UnwrapCurrent()
|
||
|
{
|
||
|
ThreadManager::Instance()->SetCurrentThread(nullptr);
|
||
|
thread_.reset();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
Thread::Delete()
|
||
|
{
|
||
|
Stop();
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
Thread::ProcessMessages(int cmsLoop)
|
||
|
{
|
||
|
int64_t msEnd = kForever == cmsLoop ? 0 : TimeAfterMillis(cmsLoop);
|
||
|
int64_t cmsNext = cmsLoop;
|
||
|
while (true) {
|
||
|
auto task = Get(cmsNext);
|
||
|
if (!task) { return !IsQuitting(); }
|
||
|
Dispatch(std::move(task));
|
||
|
if (cmsLoop != kForever) {
|
||
|
cmsNext = static_cast<int>(TimeUntilMillis(msEnd));
|
||
|
if (cmsNext < 0) { return true; }
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* static */ std::unique_ptr<Thread>
|
||
|
Thread::CreateWithSocketServer()
|
||
|
{
|
||
|
return std::unique_ptr<Thread>(new Thread(CreateDefaultSocketServer()));
|
||
|
}
|
||
|
|
||
|
/* static */ std::unique_ptr<Thread>
|
||
|
Thread::Create()
|
||
|
{
|
||
|
auto socket_server = std::unique_ptr<SocketServer>(new NullSocketServer);
|
||
|
return std::unique_ptr<Thread>(new Thread(std::move(socket_server)));
|
||
|
}
|
||
|
|
||
|
Thread *
|
||
|
Thread::Current()
|
||
|
{
|
||
|
ThreadManager *manager = ThreadManager::Instance();
|
||
|
Thread *thread = manager->CurrentThread();
|
||
|
return thread;
|
||
|
}
|
||
|
|
||
|
AutoSocketServerThread::AutoSocketServerThread(SocketServer *ss)
|
||
|
: Thread(ss, /*do_init=*/false)
|
||
|
{
|
||
|
DoInit();
|
||
|
old_thread_ = ThreadManager::Instance()->CurrentThread();
|
||
|
ThreadManager::Instance()->SetCurrentThread(nullptr);
|
||
|
ThreadManager::Instance()->SetCurrentThread(this);
|
||
|
if (old_thread_) { ThreadManager::Remove(old_thread_); }
|
||
|
}
|
||
|
|
||
|
AutoSocketServerThread::~AutoSocketServerThread()
|
||
|
{
|
||
|
Stop();
|
||
|
DoDestroy();
|
||
|
ThreadManager::Instance()->SetCurrentThread(nullptr);
|
||
|
ThreadManager::Instance()->SetCurrentThread(old_thread_);
|
||
|
if (old_thread_) { ThreadManager::Add(old_thread_); }
|
||
|
}
|
||
|
|
||
|
}// namespace sled
|