• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jordanvrtanoski / luacpp / df1dc034-d7b3-4443-a96c-5c1d6247f4da

18 Feb 2026 11:53PM UTC coverage: 99.518% (-0.5%) from 100.0%
df1dc034-d7b3-4443-a96c-5c1d6247f4da

Pull #34

circleci

jordanvrtanoski
[IMP] Add Lua state pooling with colored pools

Implement opt-in state pooling system for high-performance scenarios:
- StatePool class for managing reusable lua_State instances
- PoolManager with predefined pool colors (default, sandboxed, minimal, io)
- Selective library loading per pool configuration
- PooledState RAII wrapper for exception-safe state management
- Thread-safe pool mode for multi-threaded applications
- Pool warmup/drain/statistics methods
- PoolExhaustedException for handling pool exhaustion

Add LuaContext integration:
- RunPooled(), RunWithEnvironmentPooled() methods
- AcquirePooledState(), ReleasePooledState() methods
- AcquirePooledStateRAII() for automatic state return
- createPool(), hasPool(), getPool() management methods

Add comprehensive documentation and examples:
- docs/2-state-pooling.md: Complete pooling documentation
- docs/1-introduction.md: Added pooling overview section
- example_StatePool.cpp: Basic pooling usage
- example_StatePoolAdvanced.cpp: Advanced patterns with threads

All 126 tests passing including 56 new pooling tests.

Signed-off-by: Jordan Vrtanoski <jordan.vrtanoski@gmail.com>
Pull Request #34: [IMP] Add Lua state pooling with colored pools

975 of 987 new or added lines in 10 files covered. (98.78%)

1 existing line in 1 file now uncovered.

2682 of 2695 relevant lines covered (99.52%)

49.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.59
/Source/Engine/StatePool.cpp
1
/*
2
   MIT License
3

4
   Copyright (c) 2021 Jordan Vrtanoski
5

6
   Permission is hereby granted, free of charge, to any person obtaining a copy
7
   of this software and associated documentation files (the "Software"), to deal
8
   in the Software without restriction, including without limitation the rights
9
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
   copies of the Software, and to permit persons to whom the Software is
11
   furnished to do so, subject to the following conditions:
12

13
   The above copyright notice and this permission notice shall be included in all
14
   copies or substantial portions of the Software.
15

16
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
   SOFTWARE.
23
   */
24

25
#include "StatePool.hpp"
26
#include "LuaTNil.hpp"
27
#include "LuaTString.hpp"
28
#include "LuaTNumber.hpp"
29
#include "LuaTBoolean.hpp"
30

31
using namespace LuaCpp::Engine;
32

33
StatePool::StatePool(std::string color, PoolConfig config)
360✔
34
        : color_(std::move(color))
360✔
35
        , config_(std::move(config))
360✔
36
        , available_()
37
        , currentSize_(0)
38
        , checkedOut_(0)
39
        , threadSafe_(false)
360✔
40
{
41
}
360✔
42

43
std::unique_ptr<LuaState> StatePool::createState() {
124✔
44
        auto state = std::make_unique<LuaState>();
248✔
45
        
46
        loadLibraries(*state);
124✔
47
        loadGlobals(*state);
124✔
48
        loadHooks(*state);
124✔
49
        
50
        return std::move(state);
248✔
51
}
52

53
void StatePool::loadLibraries(LuaState& state) {
124✔
54
        if (config_.libraries.empty()) {
124✔
55
                luaL_openlibs(state);
104✔
56
                return;
104✔
57
        }
58

59
        static const std::map<std::string, std::pair<std::string, lua_CFunction>> stdLibs = {
NEW
60
                {"base", {"_G", luaopen_base}},
×
NEW
61
                {"coroutine", {"coroutine", luaopen_coroutine}},
×
NEW
62
                {"debug", {"debug", luaopen_debug}},
×
NEW
63
                {"io", {"io", luaopen_io}},
×
NEW
64
                {"math", {"math", luaopen_math}},
×
NEW
65
                {"os", {"os", luaopen_os}},
×
NEW
66
                {"package", {"package", luaopen_package}},
×
NEW
67
                {"string", {"string", luaopen_string}},
×
NEW
68
                {"table", {"table", luaopen_table}},
×
NEW
69
                {"utf8", {"utf8", luaopen_utf8}}
×
70
        };
140✔
71

72
        bool hasBase = false;
20✔
73
        for (const auto& libName : config_.libraries) {
22✔
74
                if (libName == "base") {
20✔
75
                        hasBase = true;
18✔
76
                        break;
18✔
77
                }
78
        }
79

80
        if (!hasBase) {
20✔
81
                luaL_requiref(state, "_G", luaopen_base, 1);
2✔
82
                lua_pop(state, 1);
2✔
83
        }
84

85
        for (const auto& libName : config_.libraries) {
70✔
86
                auto it = stdLibs.find(libName);
50✔
87
                if (it != stdLibs.end()) {
50✔
88
                        luaL_requiref(state, it->second.first.c_str(), it->second.second, 1);
50✔
89
                        lua_pop(state, 1);
50✔
90
                }
91
        }
92
}
93

94
void StatePool::loadGlobals(LuaState& state) {
124✔
95
        for (const auto& var : config_.globalVariables) {
126✔
96
                var.second->PushGlobal(state, var.first);
2✔
97
        }
98
}
124✔
99

100
void StatePool::loadHooks(LuaState& state) {
124✔
101
        for (const auto& hook : config_.hooks) {
134✔
102
                int mask = 0;
10✔
103
                const std::string& hookType = std::get<0>(hook);
10✔
104
                int count = std::get<1>(hook);
10✔
105
                lua_Hook hookFunc = std::get<2>(hook);
10✔
106

107
                if (hookType == "call") {
10✔
108
                        mask = LUA_MASKCALL;
4✔
109
                } else if (hookType == "return") {
6✔
110
                        mask = LUA_MASKRET;
2✔
111
                } else if (hookType == "line") {
4✔
112
                        mask = LUA_MASKLINE;
2✔
113
                } else if (hookType == "count") {
2✔
114
                        mask = LUA_MASKCOUNT;
2✔
115
                }
116

117
                lua_sethook(state, hookFunc, mask, count);
10✔
118
        }
119
}
124✔
120

121
void StatePool::resetState(LuaState& state) {
116✔
122
        lua_settop(state, 0);
116✔
123
        
124
        lua_sethook(state, nullptr, 0, 0);
116✔
125
        
126
        for (const auto& var : config_.globalVariables) {
118✔
127
                var.second->PushGlobal(state, var.first);
2✔
128
        }
129
}
116✔
130

131
std::unique_ptr<LuaState> StatePool::acquire() {
118✔
132
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
236✔
133
        if (threadSafe_) {
118✔
134
                lock.lock();
10✔
135
        }
136

137
        if (!available_.empty()) {
118✔
138
                std::unique_ptr<LuaState> state = std::move(available_.front());
68✔
139
                available_.pop();
34✔
140
                checkedOut_++;
34✔
141
                return std::move(state);
34✔
142
        }
143

144
        if (currentSize_ < config_.maxSize) {
84✔
145
                currentSize_++;
82✔
146
                checkedOut_++;
82✔
147
                if (threadSafe_) {
82✔
148
                        lock.unlock();
4✔
149
                }
150
                return createState();
82✔
151
        }
152

153
        throw PoolExhaustedException(color_);
2✔
154
}
155

156
void StatePool::release(std::unique_ptr<LuaState> state) {
116✔
157
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
232✔
158
        if (threadSafe_) {
116✔
159
                lock.lock();
10✔
160
        }
161

162
        checkedOut_--;
116✔
163

164
        resetState(*state);
116✔
165
        available_.push(std::move(state));
116✔
166

167
        if (threadSafe_) {
116✔
168
                cv_.notify_one();
10✔
169
        }
170
}
116✔
171

172
void StatePool::warmup(size_t n) {
18✔
173
        size_t toCreate = std::min(n, config_.maxSize - currentSize_);
18✔
174
        
175
        for (size_t i = 0; i < toCreate; i++) {
60✔
176
                auto state = createState();
84✔
177
                
178
                std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
84✔
179
                if (threadSafe_) {
42✔
180
                        lock.lock();
18✔
181
                }
182
                
183
                currentSize_++;
42✔
184
                available_.push(std::move(state));
42✔
185
        }
186
}
18✔
187

188
void StatePool::drain() {
4✔
189
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
8✔
190
        if (threadSafe_) {
4✔
191
                lock.lock();
2✔
192
        }
193

194
        while (!available_.empty()) {
16✔
195
                available_.pop();
12✔
196
                currentSize_--;
12✔
197
        }
198
}
4✔
199

200
const std::string& StatePool::getColor() const {
16✔
201
        return color_;
16✔
202
}
203

204
const PoolConfig& StatePool::getConfig() const {
2✔
205
        return config_;
2✔
206
}
207

208
size_t StatePool::getMaxSize() const {
10✔
209
        return config_.maxSize;
10✔
210
}
211

212
size_t StatePool::getCurrentSize() const {
44✔
213
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
44✔
214
        if (threadSafe_) {
44✔
215
                lock.lock();
20✔
216
        }
217
        return currentSize_;
88✔
218
}
219

220
size_t StatePool::availableCount() const {
52✔
221
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
104✔
222
        if (threadSafe_) {
52✔
223
                lock.lock();
20✔
224
        }
225
        return available_.size();
104✔
226
}
227

228
size_t StatePool::checkedOutCount() const {
16✔
229
        std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
16✔
230
        if (threadSafe_) {
16✔
231
                lock.lock();
6✔
232
        }
233
        return checkedOut_;
32✔
234
}
235

236
void StatePool::setThreadSafe(bool threadSafe) {
82✔
237
        threadSafe_ = threadSafe;
82✔
238
}
82✔
239

240
bool StatePool::isThreadSafe() const {
18✔
241
        return threadSafe_;
18✔
242
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc