1
0
mirror of https://github.com/codership/wsrep-lib.git synced 2025-07-28 20:02:00 +03:00

Implemented thread service support.

Added a wsrep::thread_service interface to allow application to
inject instrumented thread, mutex and condition variable implementation
for provider.

The interface is defined in include/wsrep/thread_service.hpp.
Sample implementation is provided in dbsim/db_threads.[h|c]pp.

This patch will also clean up some remaining dependencies to
wsrep-API compilation units so that the dependency to wsrep-API
is header only. This will extending the provider support to
later wsrep-API versions.
This commit is contained in:
Teemu Ollakka
2019-02-16 17:09:18 +02:00
parent 477a71dd46
commit eb4cf86c1e
33 changed files with 1821 additions and 58 deletions

View File

@ -12,6 +12,7 @@ add_executable(dbsim
db_server_state.cpp
db_simulator.cpp
db_storage_engine.cpp
db_threads.cpp
dbsim.cpp
)

View File

@ -72,7 +72,16 @@ db::params db::parse_args(int argc, char** argv)
("debug-log-level", po::value<int>(&params.debug_log_level),
"debug logging level: 0 - none, 1 - verbose")
("fast-exit", po::value<int>(&params.fast_exit),
"exit from simulation without graceful shutdown");
"exit from simulation without graceful shutdown")
("ti",
po::value<int>(&params.thread_instrumentation),
"use instrumentation for threads/mutexes/condition variables"
"(0 default disabled, 1 total counts, 2 per object)")
("ti-cond-checks",
po::value<bool>(&params.cond_checks),
"Enable checks for correct condition variable use. "
" Effective only if thread-instrumentation is enabled")
;
try
{
po::variables_map vm;
@ -86,8 +95,14 @@ db::params db::parse_args(int argc, char** argv)
validate_params(params);
}
catch (const po::error& e)
{
std::cerr << "Error parsing arguments: " << e.what() << "\n";
exit(1);
}
catch (...)
{
std::cerr << "Error parsing arguments\n";
std::cerr << desc << "\n";
exit(1);
}

View File

@ -37,6 +37,8 @@ namespace db
std::string wsrep_provider_options;
int debug_log_level;
int fast_exit;
int thread_instrumentation;
bool cond_checks;
params()
: n_servers(0)
, n_clients(0)
@ -48,6 +50,8 @@ namespace db
, wsrep_provider_options()
, debug_log_level(0)
, fast_exit(0)
, thread_instrumentation()
, cond_checks()
{ }
};

View File

@ -102,7 +102,6 @@ void db::server::stop_clients()
void db::server::client_thread(const std::shared_ptr<db::client>& client)
{
client->store_globals();
client->start();
}

View File

@ -19,12 +19,15 @@
#include "db_simulator.hpp"
#include "db_client.hpp"
#include "db_threads.hpp"
#include "wsrep/logger.hpp"
#include <boost/filesystem.hpp>
#include <sstream>
static db::ti thread_instrumentation;
void db::simulator::run()
{
start();
@ -32,6 +35,7 @@ void db::simulator::run()
std::flush(std::cerr);
std::cout << "Results:\n";
std::cout << stats() << std::endl;
std::cout << db::ti::stats() << std::endl;
}
void db::simulator::sst(db::server& server,
@ -108,6 +112,8 @@ std::string db::simulator::stats() const
void db::simulator::start()
{
thread_instrumentation.level(params_.thread_instrumentation);
thread_instrumentation.cond_checks(params_.cond_checks);
wsrep::log_info() << "Provider: " << params_.wsrep_provider;
std::string cluster_address(build_cluster_address());
@ -139,13 +145,17 @@ void db::simulator::start()
server.server_state().debug_log_level(params_.debug_log_level);
std::string server_options(params_.wsrep_provider_options);
if (server.server_state().load_provider(
params_.wsrep_provider, server_options))
wsrep::provider::services services;
services.thread_service = params_.thread_instrumentation
? &thread_instrumentation
: nullptr;
if (server.server_state().load_provider(params_.wsrep_provider,
server_options, services))
{
throw wsrep::runtime_error("Failed to load provider");
}
if (server.server_state().connect("sim_cluster", cluster_address, "",
i == 0))
i == 0))
{
throw wsrep::runtime_error("Failed to connect");
}
@ -185,6 +195,7 @@ void db::simulator::stop()
clients_stop_ = std::chrono::steady_clock::now();
wsrep::log_info() << "######## Stats ############";
wsrep::log_info() << stats();
std::cout << db::ti::stats() << std::endl;
wsrep::log_info() << "######## Stats ############";
if (params_.fast_exit)
{

718
dbsim/db_threads.cpp Normal file
View File

@ -0,0 +1,718 @@
/*
* Copyright (C) 2019 Codership Oy <info@codership.com>
*
* This file is part of wsrep-lib.
*
* Wsrep-lib is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wsrep-lib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wsrep-lib. If not, see <https://www.gnu.org/licenses/>.
*/
#include "db_threads.hpp"
#include "wsrep/compiler.hpp"
#include "wsrep/logger.hpp"
#include <cassert>
#include <pthread.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <map>
#include <mutex>
#include <ostream>
#include <sstream>
#include <thread>
#include <unordered_map>
#include <vector>
namespace
{
struct ti_obj
{
};
enum ti_opcode
{
oc_thread_create,
oc_thread_destroy,
oc_mutex_create,
oc_mutex_destroy,
oc_mutex_lock,
oc_mutex_trylock,
oc_mutex_unlock,
oc_cond_create,
oc_cond_destroy,
oc_cond_wait,
oc_cond_timedwait,
oc_cond_signal,
oc_cond_broadcast,
oc_max // must be the last
};
static const char* ti_opstring(enum ti_opcode op)
{
switch (op)
{
case oc_thread_create: return "thread_create";
case oc_thread_destroy: return "thread_destroy";
case oc_mutex_create: return "mutex_create";
case oc_mutex_destroy: return "mutex_destroy";
case oc_mutex_lock: return "mutex_lock";
case oc_mutex_trylock: return "mutex_trylock";
case oc_mutex_unlock: return "mutex_unlock";
case oc_cond_create: return "cond_create";
case oc_cond_destroy: return "cond_destroy";
case oc_cond_wait: return "cond_wait";
case oc_cond_timedwait: return "cond_timedwait";
case oc_cond_signal: return "cond_signal";
case oc_cond_broadcast: return "cond_broadcast";
default: return "unknown";
}
}
static std::vector<std::string> key_vec;
static std::atomic<int> key_cnt;
static std::vector<std::vector<size_t>> ops_map;
static std::vector<std::mutex*> ops_map_sync;
static struct ops_map_sync_deleter
{
~ops_map_sync_deleter()
{
std::for_each(ops_map_sync.begin(), ops_map_sync.end(),
[](auto entry) { delete entry; });
}
} ops_map_sync_deleter;
static std::array<std::atomic<size_t>, oc_max> total_ops;
static std::atomic<size_t> total_allocations;
static std::atomic<size_t> mutex_contention;
static std::unordered_map<std::string, size_t> mutex_contention_counts;
static int op_level;
// Check correct condition variable usage:
// - Associated mutex must be locked when waiting for cond
// - There must be at least one waiter when signalling for condition
static bool cond_checks;
static inline void cond_check(bool condition, const char* name,
const char* message)
{
if (cond_checks && !condition)
{
wsrep::log_error() << "Condition variable check failed for '"
<< name << "': " << message;
::abort();
}
}
static inline int append_key(const char* name, const char* type)
{
key_vec.push_back(std::string(name) + "_" + type);
wsrep::log_info() << "Register key " << name << "_" << type
<< " with index " << (key_cnt + 1);
ops_map.push_back(std::vector<size_t>());
ops_map_sync.push_back(new std::mutex());
ops_map.back().resize(oc_max);
return ++key_cnt;
}
template <class Key> static inline size_t get_key_index(const Key* key)
{
size_t index(reinterpret_cast<const size_t>(key) - 1);
assert(index >= 0 && index < key_vec.size());
return index;
}
template <class Key>
static inline const char* get_key_name(const Key* key)
{
return key_vec[get_key_index(key)].c_str();
}
static inline const std::string& get_key_name_by_index(size_t index)
{
assert(index < key_vec.size());
return key_vec[index];
}
// Note: Do not refer the obj pointer in this function, it may
// have been deleted before the call.
template <class Key>
static inline void update_ops(const ti_obj* obj,
const Key* key,
enum ti_opcode op)
{
if (op_level < 1)
return;
total_ops[op] += 1;
if (op_level < 2)
return;
if (false && op == oc_mutex_destroy)
{
wsrep::log_info() << "thread: " << std::this_thread::get_id()
<< " object: " << obj
<< ": name: " << get_key_name(key)
<< " op: " << ti_opstring(op);
}
std::lock_guard<std::mutex> lock(*ops_map_sync[get_key_index(key)]);
ops_map[get_key_index(key)][op] += 1;
}
struct thread_args
{
void* this_thread;
void* (*fn)(void*);
void* args;
};
pthread_key_t this_thread_key;
struct this_thread_key_initializer
{
this_thread_key_initializer()
{
pthread_key_create(&this_thread_key, nullptr);
}
~this_thread_key_initializer()
{
pthread_key_delete(this_thread_key);
}
};
void* thread_start_fn(void* args_ptr);
class ti_thread : public ti_obj
{
public:
ti_thread(const wsrep::thread_service::thread_key* key)
: key_(key)
, th_()
, retval_()
, detached_()
{
update_ops(this, key_, oc_thread_create);
}
~ti_thread()
{
update_ops(this, key_, oc_thread_destroy);
}
ti_thread(const ti_thread&) = delete;
ti_thread& operator=(const ti_thread&) = delete;
int run(void* (*fn)(void *), void* args)
{
auto ta(new thread_args{this, fn, args});
return pthread_create(&th_, nullptr, thread_start_fn, ta);
}
int detach()
{
detached_ = true;
return pthread_detach(th_);
}
int join(void** retval)
{
return pthread_join(th_, retval);
}
bool detached() const { return detached_; }
void retval(void* retval) { retval_ = retval; }
static ti_thread* self()
{
return reinterpret_cast<ti_thread*>(
pthread_getspecific(this_thread_key));
}
int setschedparam(int policy, const struct sched_param* param)
{
return pthread_setschedparam(th_, policy, param);
}
int getschedparam(int* policy, struct sched_param* param)
{
return pthread_getschedparam(th_, policy, param);
}
int equal(ti_thread* other)
{
return pthread_equal(th_, other->th_);
}
private:
const wsrep::thread_service::thread_key* key_;
pthread_t th_;
void* retval_;
bool detached_;
};
void* thread_start_fn(void* args_ptr)
{
thread_args* ta(reinterpret_cast<thread_args*>(args_ptr));
ti_thread* thread = reinterpret_cast<ti_thread*>(ta->this_thread);
pthread_setspecific(this_thread_key, thread);
void* (*fn)(void*) = ta->fn;
void* args = ta->args;
delete ta;
void* ret((*fn)(args));
pthread_setspecific(this_thread_key, nullptr);
// If we end here the thread returned instead of calling
// pthread_exit()
if (thread->detached())
delete thread;
return ret;
}
class ti_mutex : public ti_obj
{
public:
ti_mutex(const wsrep::thread_service::mutex_key* key, bool inplace)
: mutex_(PTHREAD_MUTEX_INITIALIZER)
, key_(key)
, inplace_(inplace)
#ifndef NDEBUG
, locked_()
, owner_()
#endif // ! NDEBUG
{
update_ops(this, key_, oc_mutex_create);
if (not inplace) total_allocations++;
}
~ti_mutex() { update_ops(this, key_, oc_mutex_destroy); }
ti_mutex& operator=(const ti_mutex&) = delete;
ti_mutex(const ti_mutex&) = delete;
int lock()
{
update_ops(this, key_, oc_mutex_lock);
int ret(pthread_mutex_trylock(&mutex_));
if (ret == EBUSY)
{
mutex_contention++;
{
std::lock_guard<std::mutex> lock(*ops_map_sync[get_key_index(key_)]);
mutex_contention_counts[get_key_name(key_)] += 1;
}
ret = pthread_mutex_lock(&mutex_);
}
#ifndef NDEBUG
if (ret == 0)
{
assert(owner_ == std::thread::id());
locked_ = true;
owner_ = std::this_thread::get_id();
}
#endif // ! NDEBUG
return ret;
}
int trylock()
{
update_ops(this, key_, oc_mutex_trylock);
int ret(pthread_mutex_trylock(&mutex_));
#ifndef NDEBUG
if (ret == 0)
{
assert(owner_ == std::thread::id());
locked_ = true;
owner_ = std::this_thread::get_id();
}
#endif // ! NDEBUG
return ret;
}
int unlock()
{
assert(locked_);
#ifndef NDEBUG
assert(owner_ == std::this_thread::get_id());
owner_ = std::thread::id();
#endif // ! NDEBUG
// Use temporary object. After mutex is unlocked it may be
// destroyed before this update_ops() finishes.
auto key(key_);
int ret(pthread_mutex_unlock(&mutex_));
update_ops(this, key, oc_mutex_unlock);
return ret;
}
struct condwait_context
{
#ifndef NDEBUG
bool locked;
std::thread::id owner;
#endif // ! NDEBUG
};
condwait_context save_for_condwait()
{
#ifndef NDEBUG
return condwait_context{ locked_, owner_ };
#else
return condwait_context{};
#endif // ! NDEBUG
}
void reset()
{
#ifndef NDEBUG
locked_ = false;
owner_ = std::thread::id();
#endif // ! NDEBUG
}
void restore_from_condwait(const condwait_context& ctx WSREP_UNUSED)
{
#ifndef NDEBUG
locked_ = ctx.locked;
owner_ = ctx.owner;
#endif // ! NDEBUG
}
pthread_mutex_t* native_handle() { return &mutex_; }
const wsrep::thread_service::mutex_key* key() const { return key_; }
bool inplace() const { return inplace_; }
private:
pthread_mutex_t mutex_;
const wsrep::thread_service::mutex_key* key_;
const bool inplace_;
#ifndef NDEBUG
bool locked_;
std::atomic<std::thread::id> owner_;
#endif // ! NDEBU
};
class ti_cond : public ti_obj
{
public:
ti_cond(const wsrep::thread_service::cond_key* key, bool inplace)
: cond_(PTHREAD_COND_INITIALIZER)
, key_(key)
, inplace_(inplace)
, waiter_()
{
update_ops(this, key_, oc_cond_create);
if (not inplace) total_allocations++;
}
~ti_cond() { update_ops(this, key_, oc_cond_destroy); }
ti_cond& operator=(const ti_cond&) = delete;
ti_cond(const ti_cond&) = delete;
int wait(ti_mutex& mutex)
{
cond_check(pthread_mutex_trylock(mutex.native_handle()),
get_key_name(key_), "Mutex not locked in cond wait");
waiter_ = true;
update_ops(this, key_, oc_cond_wait);
// update_ops(&mutex, mutex.key(), oc_mutex_unlock);
auto condwait_ctx(mutex.save_for_condwait());
mutex.reset();
int ret(pthread_cond_wait(&cond_, mutex.native_handle()));
// update_ops(&mutex, mutex.key(), oc_mutex_lock);
mutex.restore_from_condwait(condwait_ctx);
waiter_ = false;
return ret;
}
int timedwait(ti_mutex& mutex, const struct timespec* ts)
{
cond_check(pthread_mutex_trylock(mutex.native_handle()),
get_key_name(key_), "Mutex not locked in cond wait");
waiter_ = true;
update_ops(this, key_, oc_cond_timedwait);
// update_ops(&mutex, mutex.key(), oc_mutex_unlock);
auto condwait_ctx(mutex.save_for_condwait());
mutex.reset();
int ret(pthread_cond_timedwait(&cond_, mutex.native_handle(), ts));
// update_ops(&mutex, mutex.key(), oc_mutex_lock);
mutex.restore_from_condwait(condwait_ctx);
waiter_ = false;
return ret;
}
int signal()
{
update_ops(this, key_, oc_cond_signal);
cond_check(waiter_, get_key_name(key_),
"Signalling condition variable without waiter");
return pthread_cond_signal(&cond_);
}
int broadcast()
{
update_ops(this, key_, oc_cond_broadcast);
return pthread_cond_broadcast(&cond_);
}
bool inplace() const { return inplace_; }
private:
pthread_cond_t cond_;
const wsrep::thread_service::cond_key* key_;
const bool inplace_;
bool waiter_;
};
}
int db::ti::before_init()
{
wsrep::log_info() << "db::ti::before_init()";
return 0;
}
int db::ti::after_init()
{
wsrep::log_info() << "db::ti::after_init()";
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// Thread //
//////////////////////////////////////////////////////////////////////////////
const wsrep::thread_service::thread_key*
db::ti::create_thread_key(const char* name) WSREP_NOEXCEPT
{
assert(name);
return reinterpret_cast<const wsrep::thread_service::thread_key*>(
append_key(name, "thread"));
}
int db::ti::create_thread(const wsrep::thread_service::thread_key* key,
wsrep::thread_service::thread** thread,
void* (*fn)(void*), void* args) WSREP_NOEXCEPT
{
auto pit(new ti_thread(key));
total_allocations++;
int ret;
if ((ret = pit->run(fn, args)))
{
delete pit;
}
else
{
*thread = reinterpret_cast<wsrep::thread_service::thread*>(pit);
}
return ret;
}
int db::ti::detach(wsrep::thread_service::thread* thread) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_thread*>(thread)->detach();
}
void db::ti::exit(wsrep::thread_service::thread* thread, void* retval) WSREP_NOEXCEPT
{
ti_thread* th(reinterpret_cast<ti_thread*>(thread));
th->retval(retval);
if (th->detached())
delete th;
pthread_exit(retval);
}
int db::ti::equal(wsrep::thread_service::thread* thread_1,
wsrep::thread_service::thread* thread_2) WSREP_NOEXCEPT
{
return (reinterpret_cast<ti_thread*>(thread_1)->equal(
reinterpret_cast<ti_thread*>(thread_2)));
}
int db::ti::join(wsrep::thread_service::thread* thread, void** retval) WSREP_NOEXCEPT
{
ti_thread* th(reinterpret_cast<ti_thread*>(thread));
int ret(th->join(retval));
if (not th->detached())
{
delete th;
}
return ret;
}
wsrep::thread_service::thread* db::ti::self() WSREP_NOEXCEPT
{
return reinterpret_cast<wsrep::thread_service::thread*>(ti_thread::self());
}
int db::ti::setschedparam(wsrep::thread_service::thread* thread,
int policy, const struct sched_param* param) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_thread*>(thread)->setschedparam(policy, param);
}
int db::ti::getschedparam(wsrep::thread_service::thread* thread,
int* policy, struct sched_param* param) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_thread*>(thread)->getschedparam(policy, param);
}
//////////////////////////////////////////////////////////////////////////////
// Mutex //
//////////////////////////////////////////////////////////////////////////////
const wsrep::thread_service::mutex_key*
db::ti::create_mutex_key(const char* name) WSREP_NOEXCEPT
{
assert(name);
return reinterpret_cast<const wsrep::thread_service::mutex_key*>(
append_key(name, "mutex"));
}
wsrep::thread_service::mutex*
db::ti::init_mutex(const wsrep::thread_service::mutex_key* key, void* memblock,
size_t memblock_size) WSREP_NOEXCEPT
{
return reinterpret_cast<wsrep::thread_service::mutex*>(
memblock_size >= sizeof(ti_mutex) ? new (memblock) ti_mutex(key, true)
: new ti_mutex(key, false));
}
int db::ti::destroy(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT
{
ti_mutex* m(reinterpret_cast<ti_mutex*>(mutex));
if (m->inplace())
{
m->~ti_mutex();
}
else
{
delete m;
}
return 0;
}
int db::ti::lock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_mutex*>(mutex)->lock();
}
int db::ti::trylock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_mutex*>(mutex)->trylock();
}
int db::ti::unlock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_mutex*>(mutex)->unlock();
}
//////////////////////////////////////////////////////////////////////////////
// Cond //
//////////////////////////////////////////////////////////////////////////////
const wsrep::thread_service::cond_key* db::ti::create_cond_key(const char* name) WSREP_NOEXCEPT
{
assert(name);
return reinterpret_cast<const wsrep::thread_service::cond_key*>(
append_key(name, "cond"));
}
wsrep::thread_service::cond*
db::ti::init_cond(const wsrep::thread_service::cond_key* key, void* memblock,
size_t memblock_size) WSREP_NOEXCEPT
{
return reinterpret_cast<wsrep::thread_service::cond*>(
memblock_size >= sizeof(ti_cond) ? new (memblock) ti_cond(key, true)
: new ti_cond(key, false));
}
int db::ti::destroy(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT
{
ti_cond* c(reinterpret_cast<ti_cond*>(cond));
if (c->inplace())
{
c->~ti_cond();
}
else
{
delete c;
}
return 0;
}
int db::ti::wait(wsrep::thread_service::cond* cond,
wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_cond*>(cond)->wait(
*reinterpret_cast<ti_mutex*>(mutex));
}
int db::ti::timedwait(wsrep::thread_service::cond* cond,
wsrep::thread_service::mutex* mutex,
const struct timespec* ts) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_cond*>(cond)->timedwait(
*reinterpret_cast<ti_mutex*>(mutex), ts);
}
int db::ti::signal(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_cond*>(cond)->signal();
}
int db::ti::broadcast(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT
{
return reinterpret_cast<ti_cond*>(cond)->broadcast();
}
void db::ti::level(int level)
{
::op_level = level;
}
void db::ti::cond_checks(bool cond_checks)
{
if (cond_checks)
wsrep::log_info() << "Enabling condition variable checking";
::cond_checks = cond_checks;
}
std::string db::ti::stats()
{
std::ostringstream os;
os << "Totals:\n";
for (size_t i(0); i < total_ops.size(); ++i)
{
if (total_ops[i] > 0)
{
os << " " << ti_opstring(static_cast<enum ti_opcode>(i)) << ": "
<< total_ops[i] << "\n";
}
}
os << "Total allocations: " << total_allocations << "\n";
os << "Mutex contention: " << mutex_contention << "\n";
for (auto i : mutex_contention_counts)
{
os << " " << i.first << ": " << i.second << "\n";
}
os << "Per key:\n";
std::map<std::string, std::vector<size_t>> sorted;
for (size_t i(0); i < ops_map.size(); ++i)
{
sorted.insert(std::make_pair(get_key_name_by_index(i), ops_map[i]));
}
for (auto i : sorted)
{
for (size_t j(0); j < i.second.size(); ++j)
{
if (i.second[j])
{
os << " " << i.first << ": "
<< ti_opstring(static_cast<enum ti_opcode>(j)) << ": "
<< i.second[j] << "\n";
}
}
}
return os.str();
}

85
dbsim/db_threads.hpp Normal file
View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 Codership Oy <info@codership.com>
*
* This file is part of wsrep-lib.
*
* Wsrep-lib is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wsrep-lib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wsrep-lib. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef WSREP_DB_THREADS_HPP
#define WSREP_DB_THREADS_HPP
#include "wsrep/thread_service.hpp"
#include <string>
namespace db
{
class ti : public wsrep::thread_service
{
public:
ti() { }
int before_init() override;
int after_init() override;
/* Thread */
const wsrep::thread_service::thread_key*
create_thread_key(const char* name) WSREP_NOEXCEPT override;
int create_thread(const wsrep::thread_service::thread_key* key,
wsrep::thread_service::thread**,
void* (*fn)(void*), void*) WSREP_NOEXCEPT override;
int detach(wsrep::thread_service::thread*) WSREP_NOEXCEPT override;
int equal(wsrep::thread_service::thread*,
wsrep::thread_service::thread*) WSREP_NOEXCEPT override;
void exit(wsrep::thread_service::thread*, void*) WSREP_NOEXCEPT override;
int join(wsrep::thread_service::thread*, void**) WSREP_NOEXCEPT override;
wsrep::thread_service::thread* self() WSREP_NOEXCEPT override;
int setschedparam(wsrep::thread_service::thread*, int,
const struct sched_param*) WSREP_NOEXCEPT override;
int getschedparam(wsrep::thread_service::thread*, int*,
struct sched_param*) WSREP_NOEXCEPT override;
/* Mutex */
const wsrep::thread_service::mutex_key*
create_mutex_key(const char* name) WSREP_NOEXCEPT override;
wsrep::thread_service::mutex*
init_mutex(const wsrep::thread_service::mutex_key* key, void* memblock,
size_t memblock_size) WSREP_NOEXCEPT override;
int destroy(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT override;
int lock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT override;
int trylock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT override;
int unlock(wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT override;
/* Cond */
const wsrep::thread_service::cond_key*
create_cond_key(const char* name) WSREP_NOEXCEPT override;
wsrep::thread_service::cond*
init_cond(const wsrep::thread_service::cond_key* key, void* memblock,
size_t memblock_size) WSREP_NOEXCEPT override;
int destroy(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT override;
int wait(wsrep::thread_service::cond* cond,
wsrep::thread_service::mutex* mutex) WSREP_NOEXCEPT override;
int timedwait(wsrep::thread_service::cond* cond,
wsrep::thread_service::mutex* mutex,
const struct timespec* ts) WSREP_NOEXCEPT override;
int signal(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT override;
int broadcast(wsrep::thread_service::cond* cond) WSREP_NOEXCEPT override;
static void level(int level);
static void cond_checks(bool cond_checks);
static std::string stats();
};
}
#endif // WSREP_DB_THREADS_HPP