1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
#pragma once
#include "cancel.hpp"
#include "deep.hpp"
#include "universe.hpp"
struct Keeper;
// #################################################################################################
// xxh64 of string "kLindaBatched" generated at https://www.pelock.com/products/hash-calculator
static constexpr UniqueKey kLindaBatched{ 0xB8234DF772646567ull, "linda.batched" };
// #################################################################################################
DECLARE_UNIQUE_TYPE(LindaGroup, int);
class Linda final
: public DeepPrelude // Deep userdata MUST start with this header
{
public:
class [[nodiscard]] KeeperOperationInProgress final
{
private:
Linda& linda;
[[maybe_unused]] lua_State* const L; // just here for inspection while debugging
public:
KeeperOperationInProgress(Linda& linda_, lua_State* const L_)
: linda{ linda_ }
, L{ L_ }
{
[[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_add(1, std::memory_order_seq_cst) };
}
public:
~KeeperOperationInProgress()
{
[[maybe_unused]] UnusedInt const _prev{ linda.keeperOperationCount.fetch_sub(1, std::memory_order_seq_cst) };
}
};
enum class [[nodiscard]] Status
{
Active,
Cancelled
};
using enum Status;
private:
static constexpr size_t kEmbeddedNameLength = 24;
using EmbeddedName = std::array<char, kEmbeddedNameLength>;
// depending on the name length, it is either embedded inside the Linda, or allocated separately
std::variant<std::string_view, EmbeddedName> nameVariant{};
// counts the keeper operations in progress
std::atomic<int> keeperOperationCount{};
public:
std::condition_variable readHappened{};
std::condition_variable writeHappened{};
Universe* const U{ nullptr }; // the universe this linda belongs to
KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda
Status cancelStatus{ Status::Active };
public:
[[nodiscard]]
static void* operator new(size_t size_, Universe* U_) noexcept { return U_->internalAllocator.alloc(size_); }
// always embedded somewhere else or "in-place constructed" as a full userdata
// can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
static void operator delete(void* p_, Universe* U_) { U_->internalAllocator.free(p_, sizeof(Linda)); }
// this one is for us, to make sure memory is freed by the correct allocator
static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internalAllocator.free(p_, sizeof(Linda)); }
~Linda();
Linda(Universe* U_, LindaGroup group_, std::string_view const& name_);
Linda() = delete;
// non-copyable, non-movable
Linda(Linda const&) = delete;
Linda(Linda const&&) = delete;
Linda& operator=(Linda const&) = delete;
Linda& operator=(Linda const&&) = delete;
private:
[[nodiscard]]
static Linda* CreateTimerLinda(lua_State* L_);
static void DeleteTimerLinda(lua_State* L_, Linda* linda_);
void freeAllocatedName();
void setName(std::string_view const& name_);
public:
[[nodiscard]]
Keeper* acquireKeeper() const;
[[nodiscard]]
static Linda* CreateTimerLinda(lua_State* const L_, Passkey<Universe> const) { return CreateTimerLinda(L_); }
static void DeleteTimerLinda(lua_State* const L_, Linda* const linda_, Passkey<Universe> const) { DeleteTimerLinda(L_, linda_); }
[[nodiscard]]
std::string_view getName() const;
[[nodiscard]]
bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; }
template <typename T = uintptr_t>
[[nodiscard]]
T obfuscated() const
{
// xxh64 of string "kObfuscator" generated at https://www.pelock.com/products/hash-calculator
static constexpr UniqueKey kObfuscator{ 0x7B8AA1F99A3BD782ull };
return std::bit_cast<T>(std::bit_cast<uintptr_t>(this) ^ kObfuscator.storage);
};
void releaseKeeper(Keeper* keeper_) const;
[[nodiscard]]
static int ProtectedCall(lua_State* L_, lua_CFunction f_);
void pushCancelString(lua_State* L_) const;
[[nodiscard]]
KeeperOperationInProgress startKeeperOperation(lua_State* const L_) { return KeeperOperationInProgress{ *this, L_ }; };
[[nodiscard]]
Keeper* whichKeeper() const { return U->keepers.getKeeper(keeperIndex); }
};
|