make thread safety a compile-time toggle

This commit is contained in:
Daniel Kolesa 2022-04-21 04:54:47 +02:00
parent b430cf7c83
commit dc507f80dd
8 changed files with 98 additions and 29 deletions

View file

@ -23,6 +23,18 @@
# include <span>
#endif
#ifndef LIBCUBESCRIPT_CONF_THREAD_SAFE
/** @brief Controls thread safety of the implementation.
*
* By default, libcubescript is thread safe. That means using locking where
* necessary as well as atomic variables where necessary. If you do not need
* that, you can disable this by overriding this macro to 0 in your user
* configuration. This does not make any difference in behavior when used
* in single-threaded scenarios, other than possibly better performance.
*/
#define LIBCUBESCRIPT_CONF_THREAD_SAFE 1
#endif
namespace cubescript {
#if !defined(LIBCUBESCRIPT_CONF_USER_INTEGER)
/** @brief The integer type used.

View file

@ -12,14 +12,14 @@ namespace cubescript {
template<typename T>
static inline T var_load(unsigned char const *base) noexcept {
std::atomic<T> const *p{};
atomic_type<T> const *p{};
std::memcpy(&p, &base, sizeof(void *));
return p->load();
}
template<typename T>
static inline void var_store(unsigned char *base, T v) noexcept {
std::atomic<T> *p{};
atomic_type<T> *p{};
std::memcpy(&p, &base, sizeof(void *));
p->store(v);
}
@ -29,24 +29,24 @@ static inline void var_store(unsigned char *base, T v) noexcept {
*/
var_value::var_value(integer_type v): p_type{value_type::INTEGER} {
new (p_stor) std::atomic<integer_type>{v};
new (p_ostor) std::atomic<integer_type>{0};
new (p_stor) atomic_type<integer_type>{v};
new (p_ostor) atomic_type<integer_type>{0};
}
var_value::var_value(float_type v): p_type{value_type::FLOAT} {
FS vs{};
std::memcpy(&vs, &v, sizeof(v));
new (p_stor) std::atomic<FS>{vs};
new (p_ostor) std::atomic<FS>{0};
new (p_stor) atomic_type<FS>{vs};
new (p_ostor) atomic_type<FS>{0};
}
var_value::var_value(std::string_view const &v, state &cs):
p_type{value_type::STRING}
{
new (p_stor) std::atomic<char const *>{
new (p_stor) atomic_type<char const *>{
state_p{cs}.ts().istate->strman->add(v)
};
new (p_ostor) std::atomic<char const *>{nullptr};
new (p_ostor) atomic_type<char const *>{nullptr};
}
var_value::~var_value() {

View file

@ -3,8 +3,9 @@
#include <cubescript/cubescript.hh>
#include "cs_lock.hh"
#include <bitset>
#include <atomic>
#include <memory>
namespace cubescript {
@ -46,9 +47,9 @@ struct var_value {
private:
using VU = union {
std::atomic<integer_type> i;
std::atomic<FS> f;
std::atomic<char const *> s;
atomic_type<integer_type> i;
atomic_type<FS> f;
atomic_type<char const *> s;
};
/* fixed upon creation */

54
src/cs_lock.hh Normal file
View file

@ -0,0 +1,54 @@
#ifndef LIBCUBESCRIPT_LOCK_HH
#define LIBCUBESCRIPT_LOCK_HH
#include <cubescript/cubescript.hh>
#if LIBCUBESCRIPT_CONF_THREAD_SAFE
#include <mutex>
#include <atomic>
#endif
namespace cubescript {
#if ! LIBCUBESCRIPT_CONF_THREAD_SAFE
struct mutex_type {
void lock() {}
void unlock() {}
};
template<typename T>
struct atomic_type {
T p_v;
T load() const {
return p_v;
}
void store(T v) {
p_v = v;
}
};
#else
using mutex_type = std::mutex;
template<typename T>
using atomic_type = std::atomic<T>;
#endif
struct mtx_guard {
mtx_guard(mutex_type &m): p_m{m} {
m.lock();
}
~mtx_guard() {
p_m.unlock();
}
mutex_type &p_m;
};
} /* namespace cubescript */
#endif

View file

@ -9,6 +9,7 @@
#include "cs_vm.hh"
#include "cs_parser.hh"
#include "cs_error.hh"
#include "cs_lock.hh"
namespace cubescript {
@ -48,7 +49,7 @@ ident *internal_state::lookup_ident(std::size_t idx) {
if (idx < MAX_ARGUMENTS) {
return argmap[idx];
}
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
return identmap[idx];
}
@ -56,12 +57,12 @@ ident const *internal_state::lookup_ident(std::size_t idx) const {
if (idx < MAX_ARGUMENTS) {
return argmap[idx];
}
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
return identmap[idx];
}
std::size_t internal_state::get_identnum() const {
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
return identmap.size();
}
@ -70,7 +71,7 @@ void internal_state::foreach_ident(void (*f)(ident *, void *), void *data) {
for (std::size_t i = 0; i < nids; ++i) {
ident *id;
{
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
id = identmap[i];
}
f(id, data);
@ -83,7 +84,7 @@ ident *internal_state::add_ident(ident *id, ident_impl *impl) {
}
ident_p{*id}.impl(impl);
{
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
idents[id->name()] = id;
impl->p_index = int(identmap.size());
identmap.push_back(id);
@ -108,7 +109,7 @@ ident &internal_state::new_ident(state &cs, std::string_view name, int flags) {
}
ident *internal_state::get_ident(std::string_view name) const {
std::lock_guard<std::mutex> l{ident_mtx};
mtx_guard l{ident_mtx};
auto id = idents.find(name);
if (id == idents.end()) {
return nullptr;

View file

@ -7,10 +7,10 @@
#include <string>
#include <vector>
#include <array>
#include <mutex>
#include "cs_bcode.hh"
#include "cs_ident.hh"
#include "cs_lock.hh"
namespace cubescript {
@ -52,7 +52,7 @@ struct internal_state {
> idents;
std::vector<ident *, std_allocator<ident *>> identmap;
std::array<ident *, MAX_ARGUMENTS> argmap;
mutable std::mutex ident_mtx;
mutable mutex_type ident_mtx;
string_pool *strman;
empty_block *empty;

View file

@ -4,6 +4,7 @@
#include "cs_strman.hh"
#include "cs_thread.hh"
#include "cs_lock.hh"
namespace cubescript {
@ -21,7 +22,7 @@ inline string_ref_state *get_ref_state(char const *ptr) {
char const *string_pool::add(std::string_view str) {
{
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
auto it = counts.find(str);
/* already present: just increment ref */
if (it != counts.end()) {
@ -43,7 +44,7 @@ char const *string_pool::add(std::string_view str) {
memcpy(strp, str.data(), ss);
/* store it */
{
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
counts.emplace(std::string_view{strp, ss}, get_ref_state(strp));
}
return strp;
@ -52,7 +53,7 @@ char const *string_pool::add(std::string_view str) {
char const *string_pool::internal_ref(char const *ptr) {
auto *ss = get_ref_state(ptr);
{
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
++ss->refcount;
}
return ptr;
@ -63,7 +64,7 @@ string_ref string_pool::steal(char *ptr) {
auto sr = std::string_view{ptr, ss->length};
string_ref_state *st = nullptr;
{
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
/* much like add(), but we already have memory */
auto it = counts.find(sr);
if (it != counts.end()) {
@ -78,7 +79,7 @@ string_ref string_pool::steal(char *ptr) {
std::memcpy(&rp, &st, sizeof(rp));
return string_ref{rp};
} else {
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
ss->refcount = 0; /* string_ref will increment it */
counts.emplace(sr, ss);
}
@ -87,7 +88,7 @@ string_ref string_pool::steal(char *ptr) {
void string_pool::internal_unref(char const *ptr) {
auto *ss = get_ref_state(ptr);
if (std::lock_guard<std::mutex> l{p_mtx}; !--ss->refcount) {
if (mtx_guard l{p_mtx}; !--ss->refcount) {
/* refcount zero, so ditch it
* this path is a little slow...
*/
@ -113,7 +114,7 @@ void string_pool::internal_unref(char const *ptr) {
char const *string_pool::find(std::string_view str) const {
string_ref_state *sp;
{
std::lock_guard<std::mutex> l{p_mtx};
mtx_guard l{p_mtx};
auto it = counts.find(str);
if (it == counts.end()) {
return nullptr;

View file

@ -5,10 +5,10 @@
#include <unordered_map>
#include <string_view>
#include <mutex>
#include "cs_std.hh"
#include "cs_state.hh"
#include "cs_lock.hh"
namespace cubescript {
@ -83,7 +83,7 @@ struct string_pool {
char *alloc_buf(std::size_t len) const;
internal_state *cstate;
mutable std::mutex p_mtx{};
mutable mutex_type p_mtx{};
std::unordered_map<
std::string_view, string_ref_state *,
std::hash<std::string_view>,