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> # include <span>
#endif #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 { namespace cubescript {
#if !defined(LIBCUBESCRIPT_CONF_USER_INTEGER) #if !defined(LIBCUBESCRIPT_CONF_USER_INTEGER)
/** @brief The integer type used. /** @brief The integer type used.

View file

@ -12,14 +12,14 @@ namespace cubescript {
template<typename T> template<typename T>
static inline T var_load(unsigned char const *base) noexcept { 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 *)); std::memcpy(&p, &base, sizeof(void *));
return p->load(); return p->load();
} }
template<typename T> template<typename T>
static inline void var_store(unsigned char *base, T v) noexcept { 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 *)); std::memcpy(&p, &base, sizeof(void *));
p->store(v); 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} { var_value::var_value(integer_type v): p_type{value_type::INTEGER} {
new (p_stor) std::atomic<integer_type>{v}; new (p_stor) atomic_type<integer_type>{v};
new (p_ostor) std::atomic<integer_type>{0}; new (p_ostor) atomic_type<integer_type>{0};
} }
var_value::var_value(float_type v): p_type{value_type::FLOAT} { var_value::var_value(float_type v): p_type{value_type::FLOAT} {
FS vs{}; FS vs{};
std::memcpy(&vs, &v, sizeof(v)); std::memcpy(&vs, &v, sizeof(v));
new (p_stor) std::atomic<FS>{vs}; new (p_stor) atomic_type<FS>{vs};
new (p_ostor) std::atomic<FS>{0}; new (p_ostor) atomic_type<FS>{0};
} }
var_value::var_value(std::string_view const &v, state &cs): var_value::var_value(std::string_view const &v, state &cs):
p_type{value_type::STRING} 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) 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() { var_value::~var_value() {

View file

@ -3,8 +3,9 @@
#include <cubescript/cubescript.hh> #include <cubescript/cubescript.hh>
#include "cs_lock.hh"
#include <bitset> #include <bitset>
#include <atomic>
#include <memory> #include <memory>
namespace cubescript { namespace cubescript {
@ -46,9 +47,9 @@ struct var_value {
private: private:
using VU = union { using VU = union {
std::atomic<integer_type> i; atomic_type<integer_type> i;
std::atomic<FS> f; atomic_type<FS> f;
std::atomic<char const *> s; atomic_type<char const *> s;
}; };
/* fixed upon creation */ /* 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_vm.hh"
#include "cs_parser.hh" #include "cs_parser.hh"
#include "cs_error.hh" #include "cs_error.hh"
#include "cs_lock.hh"
namespace cubescript { namespace cubescript {
@ -48,7 +49,7 @@ ident *internal_state::lookup_ident(std::size_t idx) {
if (idx < MAX_ARGUMENTS) { if (idx < MAX_ARGUMENTS) {
return argmap[idx]; return argmap[idx];
} }
std::lock_guard<std::mutex> l{ident_mtx}; mtx_guard l{ident_mtx};
return identmap[idx]; return identmap[idx];
} }
@ -56,12 +57,12 @@ ident const *internal_state::lookup_ident(std::size_t idx) const {
if (idx < MAX_ARGUMENTS) { if (idx < MAX_ARGUMENTS) {
return argmap[idx]; return argmap[idx];
} }
std::lock_guard<std::mutex> l{ident_mtx}; mtx_guard l{ident_mtx};
return identmap[idx]; return identmap[idx];
} }
std::size_t internal_state::get_identnum() const { std::size_t internal_state::get_identnum() const {
std::lock_guard<std::mutex> l{ident_mtx}; mtx_guard l{ident_mtx};
return identmap.size(); 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) { for (std::size_t i = 0; i < nids; ++i) {
ident *id; ident *id;
{ {
std::lock_guard<std::mutex> l{ident_mtx}; mtx_guard l{ident_mtx};
id = identmap[i]; id = identmap[i];
} }
f(id, data); f(id, data);
@ -83,7 +84,7 @@ ident *internal_state::add_ident(ident *id, ident_impl *impl) {
} }
ident_p{*id}.impl(impl); ident_p{*id}.impl(impl);
{ {
std::lock_guard<std::mutex> l{ident_mtx}; mtx_guard l{ident_mtx};
idents[id->name()] = id; idents[id->name()] = id;
impl->p_index = int(identmap.size()); impl->p_index = int(identmap.size());
identmap.push_back(id); 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 { 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); auto id = idents.find(name);
if (id == idents.end()) { if (id == idents.end()) {
return nullptr; return nullptr;

View file

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

View file

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

View file

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