tunsafe-clang15/tunsafe_threading.cpp
2018-10-21 16:54:17 +02:00

166 lines
3.5 KiB
C++

// SPDX-License-Identifier: AGPL-1.0-only
// Copyright (C) 2018 Ludvig Strigeus <info@tunsafe.com>. All Rights Reserved.
#include "stdafx.h"
#include "tunsafe_threading.h"
#include <stdlib.h>
#include <assert.h>
#if defined(OS_POSIX)
Thread::Thread() {
thread_ = 0;
}
Thread::~Thread() {
assert(thread_ == 0);
}
static void *ThreadMainStatic(void *x) {
Thread::Runner *t = (Thread::Runner*)x;
t->ThreadMain();
return 0;
}
void Thread::StartThread(Runner *runner) {
assert(thread_ == 0);
if (pthread_create(&thread_, NULL, &ThreadMainStatic, runner) != 0)
tunsafe_die("pthread_create failed");
}
void Thread::StopThread() {
if (thread_) {
void *x;
pthread_join(thread_, &x);
thread_ = 0;
}
}
void Thread::DetachThread() {
if (thread_) {
pthread_detach(thread_);
thread_ = 0;
}
}
bool Thread::is_started() {
return thread_ != 0;
}
void ConditionVariable::WaitTimed(Mutex *mutex, int millis) {
#if !defined(OS_MACOSX)
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += (millis / 1000);
ts.tv_nsec += (millis % 1000) * 1000000;
if (ts.tv_nsec >= 1000000000) {
ts.tv_nsec -= 1000000000;
ts.tv_sec++;
}
pthread_cond_timedwait(&condvar_, &mutex->lock_, &ts);
#else
struct timespec ts;
ts.tv_sec = millis / 1000;
ts.tv_nsec = (millis % 1000) * 1000000;
pthread_cond_timedwait_relative_np(&condvar_, &mutex->lock_, &ts);
#endif
}
#endif // defined(OS_POSIX)
#if defined(OS_WIN)
Thread::Thread() {
thread_ = 0;
}
Thread::~Thread() {
assert(thread_ == 0);
}
static DWORD WINAPI ThreadMainStatic(void *x) {
Thread::Runner *t = (Thread::Runner*)x;
t->ThreadMain();
return 0;
}
void Thread::StartThread(Runner *runner) {
assert(thread_ == 0);
DWORD thread_id;
thread_ = CreateThread(NULL, 0, &ThreadMainStatic, (LPVOID)runner, 0, &thread_id);
}
void Thread::StopThread() {
if (thread_) {
WaitForSingleObject(thread_, INFINITE);
CloseHandle(thread_);
thread_ = 0;
}
}
void Thread::DetachThread() {
if (thread_) {
CloseHandle(thread_);
thread_ = 0;
}
}
bool Thread::is_started() {
return thread_ != 0;
}
#endif
MultithreadedDelayedDelete::MultithreadedDelayedDelete() {
table_ = NULL;
num_threads_ = 0;
}
MultithreadedDelayedDelete::~MultithreadedDelayedDelete() {
assert(curr_.size() == 0);
assert(next_.size() == 0);
assert(to_delete_.size() == 0);
free(table_);
}
void MultithreadedDelayedDelete::Configure(uint32 num_threads) {
assert(table_ == NULL);
num_threads_ = num_threads;
table_ = (CheckpointData*)calloc(sizeof(CheckpointData), num_threads);
}
void MultithreadedDelayedDelete::Add(DoDeleteFunc *func, void *param) {
if (num_threads_ == 0) {
func(param);
return;
}
lock_.Acquire();
Entry e = {func, param};
curr_.push_back(e);
lock_.Release();
}
void MultithreadedDelayedDelete::Checkpoint(uint32 thread_id) {
table_[thread_id].value.store(1);
}
void MultithreadedDelayedDelete::MainCheckpoint() {
// Wait for all threads to signal that they reached the checkpoint
for (size_t i = 0; i < num_threads_; i++) {
if (table_[i].value.load() == 0)
return;
}
// All threads reached the checkpoint, clear the values
for (size_t i = 0; i < num_threads_; i++)
table_[i].value.store(0);
// Swap curr and next, and delete all nexts.
lock_.Acquire();
std::swap(curr_, next_);
std::swap(curr_, to_delete_);
lock_.Release();
for (auto it = to_delete_.begin(); it != to_delete_.end(); ++it) {
it->func(it->param);
}
to_delete_.clear();
}