aboutsummaryrefslogtreecommitdiff
path: root/src/universe.h
blob: f4211afa3abd74a93bfc015465f311e105fb0f00 (plain)
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#pragma once

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#include "lua.h"
#ifdef __cplusplus
}
#endif // __cplusplus

#include "compat.h"
#include "macros_and_utils.h"

#include <mutex>

// ################################################################################################

// forwards
struct DeepPrelude;
struct Keepers;
class Lane;

/*
* Do we want to activate full lane tracking feature? (EXPERIMENTAL)
*/
#define HAVE_LANE_TRACKING() 1

// ################################################################################################

// everything we need to provide to lua_newstate()
class AllocatorDefinition
{
    public:

    lua_Alloc m_allocF{ nullptr };
    void* m_allocUD{ nullptr };

    static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv(L, size_, 0); }
    // 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([[maybe_unused]] void* p_, lua_State* L) { ASSERT_L(!"should never be called") };

    AllocatorDefinition(lua_Alloc allocF_, void* allocUD_) noexcept
    : m_allocF{ allocF_ }
    , m_allocUD{ allocUD_ }
    {
    }
    AllocatorDefinition() = default;
    AllocatorDefinition(AllocatorDefinition const& rhs_) = default;
    AllocatorDefinition(AllocatorDefinition&& rhs_) = default;
    AllocatorDefinition& operator=(AllocatorDefinition const& rhs_) = default;
    AllocatorDefinition& operator=(AllocatorDefinition&& rhs_) = default;

    void initFrom(lua_State* L)
    {
        m_allocF = lua_getallocf(L, &m_allocUD);
    }

    void* lua_alloc(void* ptr_, size_t osize_, size_t nsize_)
    {
        m_allocF(m_allocUD, ptr_, osize_, nsize_);
    }

    void* alloc(size_t nsize_)
    {
        return m_allocF(m_allocUD, nullptr, 0, nsize_);
    }

    void free(void* ptr_, size_t osize_)
    {
        std::ignore = m_allocF(m_allocUD, ptr_, osize_, 0);
    }
};

// ################################################################################################

// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator
class ProtectedAllocator : public AllocatorDefinition
{
    private:

    std::mutex m_lock;

    static void* protected_lua_Alloc(void* ud_, void* ptr_, size_t osize_, size_t nsize_)
    {
        ProtectedAllocator* const allocator{ static_cast<ProtectedAllocator*>(ud_) };
        std::lock_guard<std::mutex> guard{ allocator->m_lock };
        return allocator->m_allocF(allocator->m_allocUD, ptr_, osize_, nsize_);
    }

    public:

    // we are not like our base class: we can't be created inside a full userdata (or we would have to install a metatable and __gc handler to destroy ourselves properly)
    static void* operator new(size_t size_, lua_State* L) noexcept = delete;
    static void operator delete(void* p_, lua_State* L) = delete;

    AllocatorDefinition makeDefinition()
    {
        return AllocatorDefinition{ protected_lua_Alloc, this};
    }

    void installIn(lua_State* L)
    {
        lua_setallocf(L, protected_lua_Alloc, this);
    }

    void removeFrom(lua_State* L)
    {
        // remove the protected allocator, if any
        if (m_allocF != nullptr)
        {
            // install the non-protected allocator
            lua_setallocf(L, m_allocF, m_allocUD);
        }
    }
};

// ################################################################################################

// everything regarding the Lanes universe is stored in that global structure
// held as a full userdata in the master Lua state that required it for the first time
class Universe
{
    public:

#ifdef PLATFORM_LINUX
    // Linux needs to check, whether it's been run as root
    bool const m_sudo{ geteuid() == 0 };
#else
    bool const m_sudo{ false };
#endif // PLATFORM_LINUX

    // for verbose errors
    bool verboseErrors{ false };

    bool demoteFullUserdata{ false };

    // before a state is created, this function will be called to obtain the allocator
    lua_CFunction provide_allocator{ nullptr };

    // after a state is created, this function will be called right after the bases libraries are loaded
    lua_CFunction on_state_create_func{ nullptr };

    // if allocator="protected" is found in the configuration settings, a wrapper allocator will protect all allocator calls with a mutex
    // contains a mutex and the original allocator definition
    ProtectedAllocator protected_allocator;

    AllocatorDefinition internal_allocator;

    Keepers* keepers{ nullptr };

    // Initialized by 'init_once_LOCKED()': the deep userdata Linda object
    // used for timers (each lane will get a proxy to this)
    DeepPrelude* timer_deep{ nullptr };

#if HAVE_LANE_TRACKING()
    std::mutex tracking_cs;
    Lane* volatile tracking_first{ nullptr }; // will change to TRACKING_END if we want to activate tracking
#endif // HAVE_LANE_TRACKING()

    std::mutex selfdestruct_cs;

    // require() serialization
    std::recursive_mutex require_cs;

    // metatable unique identifiers
    std::atomic<lua_Integer> next_mt_id{ 1 };

#if USE_DEBUG_SPEW()
    std::atomic<int> debugspew_indent_depth{ 0 };
#endif // USE_DEBUG_SPEW()

    Lane* volatile selfdestruct_first{ nullptr };
    // After a lane has removed itself from the chain, it still performs some processing.
    // The terminal desinit sequence should wait for all such processing to terminate before force-killing threads
    std::atomic<int> selfdestructing_count{ 0 };

    Universe();
    ~Universe() = default;
    Universe(Universe const&) = delete;
    Universe(Universe&&) = delete;
    Universe& operator=(Universe const&) = delete;
    Universe& operator=(Universe&&) = delete;
};

// ################################################################################################

Universe* universe_get(lua_State* L);
Universe* universe_create(lua_State* L);
void universe_store(lua_State* L, Universe* U);