aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2025-03-17 14:54:56 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2025-03-17 14:54:56 +0100
commit7acc7867a8ebee0a3467a382b6998cb4e95580ed (patch)
tree13f55245154b235b6a207e416d712702869644a0
parenta57690123ae3ce5bdd7e970690f1380e88e4eaf6 (diff)
downloadlanes-7acc7867a8ebee0a3467a382b6998cb4e95580ed.tar.gz
lanes-7acc7867a8ebee0a3467a382b6998cb4e95580ed.tar.bz2
lanes-7acc7867a8ebee0a3467a382b6998cb4e95580ed.zip
Fix test "allocator = <bad C function>" not failing against LuaJIT like it should
-rw-r--r--src/state.cpp2
-rw-r--r--src/universe.cpp10
-rw-r--r--src/universe.hpp2
-rw-r--r--unit_tests/init_and_shutdown.cpp347
4 files changed, 185 insertions, 176 deletions
diff --git a/src/state.cpp b/src/state.cpp
index b558d11..fc7f5ef 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -158,7 +158,7 @@ namespace state {
158 // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... 158 // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate...
159 return luaL_newstate(); 159 return luaL_newstate();
160 } else { 160 } else {
161 lanes::AllocatorDefinition const _def{ U->resolveAllocator(from, hint) }; 161 lanes::AllocatorDefinition const _def{ U->resolveAndValidateAllocator(from, hint) };
162 return _def.newState(); 162 return _def.newState();
163 } 163 }
164 } 164 }
diff --git a/src/universe.cpp b/src/universe.cpp
index dd7bc4b..db00b72 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -244,7 +244,7 @@ void Universe::initializeAllocatorFunction(lua_State* const L_)
244 provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator 244 provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
245 if (provideAllocator != nullptr) { 245 if (provideAllocator != nullptr) {
246 // make sure the function doesn't have upvalues 246 // make sure the function doesn't have upvalues
247 char const* _upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval? 247 char const* _upname{ lua_getupvalue(L_, -1, 1) }; // L_: settings allocator upval?
248 if (_upname != nullptr) { // should be "" for C functions with upvalues if any 248 if (_upname != nullptr) { // should be "" for C functions with upvalues if any
249 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); 249 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
250 } 250 }
@@ -266,11 +266,11 @@ void Universe::initializeAllocatorFunction(lua_State* const L_)
266 std::ignore = luaG_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator" 266 std::ignore = luaG_getfield(L_, kIdxTop, "internal_allocator"); // L_: settings "libc"|"allocator"
267 LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation 267 LUA_ASSERT(L_, lua_isstring(L_, kIdxTop)); // should be the case due to lanes.lua parameter validation
268 std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) }; 268 std::string_view const _allocator{ luaG_tostring(L_, kIdxTop) };
269 // use whatever the provider provides. This performs validation of what provideAllocator is giving
270 // we do this even if _allocator == "libc", to have the validation part
271 internalAllocator = resolveAndValidateAllocator(L_, "internal");
269 if (_allocator == "libc") { 272 if (_allocator == "libc") {
270 internalAllocator = lanes::AllocatorDefinition{ libc_lua_Alloc, nullptr }; 273 internalAllocator = lanes::AllocatorDefinition{ libc_lua_Alloc, nullptr };
271 } else {
272 // use whatever the provider provides
273 internalAllocator = resolveAllocator(L_, "internal");
274 } 274 }
275 lua_pop(L_, 1); // L_: settings 275 lua_pop(L_, 1); // L_: settings
276 STACK_CHECK(L_, 1); 276 STACK_CHECK(L_, 1);
@@ -331,7 +331,7 @@ void Universe::initializeOnStateCreate(lua_State* const L_)
331 331
332// ################################################################################################# 332// #################################################################################################
333 333
334lanes::AllocatorDefinition Universe::resolveAllocator(lua_State* const L_, std::string_view const& hint_) const 334lanes::AllocatorDefinition Universe::resolveAndValidateAllocator(lua_State* const L_, std::string_view const& hint_) const
335{ 335{
336 lanes::AllocatorDefinition _ret{ protectedAllocator }; 336 lanes::AllocatorDefinition _ret{ protectedAllocator };
337 if (provideAllocator == nullptr) { 337 if (provideAllocator == nullptr) {
diff --git a/src/universe.hpp b/src/universe.hpp
index 2a3085d..42a3d83 100644
--- a/src/universe.hpp
+++ b/src/universe.hpp
@@ -151,7 +151,7 @@ class Universe final
151 static int InitializeFinalizer(lua_State* L_); 151 static int InitializeFinalizer(lua_State* L_);
152 void initializeOnStateCreate(lua_State* L_); 152 void initializeOnStateCreate(lua_State* L_);
153 [[nodiscard]] 153 [[nodiscard]]
154 lanes::AllocatorDefinition resolveAllocator(lua_State* L_, std::string_view const& hint_) const; 154 lanes::AllocatorDefinition resolveAndValidateAllocator(lua_State* L_, std::string_view const& hint_) const;
155 static inline void Store(lua_State* L_, Universe* U_); 155 static inline void Store(lua_State* L_, Universe* U_);
156 [[nodiscard]] 156 [[nodiscard]]
157 bool terminateFreeRunningLanes(lua_Duration shutdownTimeout_, CancelOp op_); 157 bool terminateFreeRunningLanes(lua_Duration shutdownTimeout_, CancelOp op_);
diff --git a/unit_tests/init_and_shutdown.cpp b/unit_tests/init_and_shutdown.cpp
index 9652f2d..b8174fd 100644
--- a/unit_tests/init_and_shutdown.cpp
+++ b/unit_tests/init_and_shutdown.cpp
@@ -56,234 +56,243 @@ TEST_CASE("lanes.require 'lanes'")
56} 56}
57 57
58// ################################################################################################# 58// #################################################################################################
59// #################################################################################################
60// allocator should be "protected", a C function returning a suitable userdata, or nil
61 59
62TEST_CASE("lanes.configure") 60// allocator should be "protected", a C function returning a suitable userdata, or nil
61TEST_CASE("lanes.configure.allocator")
63{ 62{
64 LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; 63 LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
65 64
65 SECTION("allocator = false")
66 {
67 L.requireFailure("require 'lanes'.configure{allocator = false}");
68 }
69
66 // --------------------------------------------------------------------------------------------- 70 // ---------------------------------------------------------------------------------------------
67 71
68 SECTION("allocator", "[allocator]") 72 SECTION("allocator = true")
69 { 73 {
70 SECTION("allocator = false") 74 L.requireFailure("require 'lanes'.configure{allocator = true}");
71 { 75 }
72 L.requireFailure("require 'lanes'.configure{allocator = false}");
73 }
74 76
75 // ----------------------------------------------------------------------------------------- 77 // ---------------------------------------------------------------------------------------------
76 78
77 SECTION("allocator = true") 79 SECTION("allocator = <number>")
78 { 80 {
79 L.requireFailure("require 'lanes'.configure{allocator = true}"); 81 L.requireFailure("require 'lanes'.configure{allocator = 33}");
80 } 82 }
81 83
82 // ----------------------------------------------------------------------------------------- 84 // ---------------------------------------------------------------------------------------------
83 85
84 SECTION("allocator = <number>") 86 SECTION("allocator = <table>")
85 { 87 {
86 L.requireFailure("require 'lanes'.configure{allocator = 33}"); 88 L.requireFailure("require 'lanes'.configure{allocator = {}}");
87 } 89 }
88 90
89 // ----------------------------------------------------------------------------------------- 91 // ---------------------------------------------------------------------------------------------
90 92
91 SECTION("allocator = <table>") 93 SECTION("allocator = <Lua function>")
92 { 94 {
93 L.requireFailure("require 'lanes'.configure{allocator = {}}"); 95 L.requireFailure("require 'lanes'.configure{allocator = function() return {}, 12, 'yoy' end}");
94 } 96 }
95 97
96 // ----------------------------------------------------------------------------------------- 98 // ---------------------------------------------------------------------------------------------
97 99
98 SECTION("allocator = <Lua function>") 100 SECTION("allocator = <bad C function>")
99 { 101 {
100 L.requireFailure("require 'lanes'.configure{allocator = function() return {}, 12, 'yoy' end}"); 102 // a C function that doesn't return what we expect should cause an error too
101 } 103 // TODO: for some reason, we use os.getenv here because using 'print' as the culprit, the tests deadlock in Release builds
104 L.requireFailure("return type(require 'lanes'.configure{allocator = os.getenv})");
105 }
102 106
103 // ----------------------------------------------------------------------------------------- 107 // ---------------------------------------------------------------------------------------------
104 108
105 SECTION("allocator = <bad C function>") 109 SECTION("allocator = <string with a typo>")
106 { 110 {
107 // a C function that doesn't return what we expect should cause an error too 111 // oops, a typo
108 L.requireFailure("require 'lanes'.configure{allocator = print}"); 112 L.requireFailure("require 'lanes'.configure{allocator = 'Protected'}");
109 } 113 }
110 114
111 // ----------------------------------------------------------------------------------------- 115 // ---------------------------------------------------------------------------------------------
112 116
113 SECTION("allocator = <string with a typo>") 117 SECTION("allocator = 'protected'")
114 { 118 {
115 // oops, a typo 119 // no typo, should work
116 L.requireFailure("require 'lanes'.configure{allocator = 'Protected'}"); 120 L.requireSuccess("require 'lanes'.configure{allocator = 'protected'}");
117 } 121 }
118 122
119 // ----------------------------------------------------------------------------------------- 123 // ---------------------------------------------------------------------------------------------
120 124
121 SECTION("allocator = 'protected'") 125 SECTION("allocator = <good custom C allocator>")
122 { 126 {
123 // no typo, should work 127 // a function that provides what we expect is fine
124 L.requireSuccess("require 'lanes'.configure{allocator = 'protected'}"); 128 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) {
125 } 129 lanes::AllocatorDefinition* const _def{ new (L_) lanes::AllocatorDefinition{} };
130 _def->initFrom(L_);
131 return 1;
132 };
133 lua_pushcfunction(L, _provideAllocator);
134 lua_setglobal(L, "ProvideAllocator");
135 L.requireSuccess("require 'lanes'.configure{allocator = ProvideAllocator}");
136 }
126 137
127 // ----------------------------------------------------------------------------------------- 138 // ---------------------------------------------------------------------------------------------
128 139
129 SECTION("allocator = <good custom C allocator>") 140 SECTION("allocator not returning an AllocatorDefinition")
130 { 141 {
131 // a function that provides what we expect is fine 142 // a function that provides something that is definitely not an AllocatorDefinition, should cause an error
132 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) { 143 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) {
133 lanes::AllocatorDefinition* const _def{ new (L_) lanes::AllocatorDefinition{} }; 144 lua_newtable(L_);
134 _def->initFrom(L_); 145 return 1;
135 return 1; 146 };
136 }; 147 lua_pushcfunction(L, _provideAllocator);
137 lua_pushcfunction(L, _provideAllocator); 148 lua_setglobal(L, "ProvideAllocator");
138 lua_setglobal(L, "ProvideAllocator"); 149 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected
139 L.requireSuccess("require 'lanes'.configure{allocator = ProvideAllocator}"); 150 // which would prevent us from calling _provideAllocator
140 } 151 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}");
152 }
141 153
142 // ----------------------------------------------------------------------------------------- 154 // ---------------------------------------------------------------------------------------------
143 155
144 SECTION("allocator not returning an AllocatorDefinition") 156 SECTION("allocator returning an AllocatorDefinition with the wrong signature")
145 { 157 {
146 // a function that provides something that is definitely not an AllocatorDefinition, should cause an error 158 // a function that provides something that is too small to contain an AllocatorDefinition, should cause an error
147 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) { 159 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) {
148 lua_newtable(L_); 160 // create a full userdata that is too small (it only contains enough to store a version tag, but not the rest
149 return 1; 161 auto* const _duck{ static_cast<lanes::AllocatorDefinition::version_t*>(lua_newuserdata(L_, sizeof(lanes::AllocatorDefinition::version_t))) };
150 }; 162 *_duck = 666777;
151 lua_pushcfunction(L, _provideAllocator); 163 return 1;
152 lua_setglobal(L, "ProvideAllocator"); 164 };
153 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected 165 lua_pushcfunction(L, _provideAllocator);
154 // which would prevent us from calling _provideAllocator 166 lua_setglobal(L, "ProvideAllocator");
155 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}"); 167 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected
156 } 168 // which would prevent us from calling _provideAllocator
169 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}");
170 }
157 171
158 // ----------------------------------------------------------------------------------------- 172 // -----------------------------------------------------------------------------------------
159 173
160 SECTION("allocator returning an AllocatorDefinition with the wrong signature") 174 SECTION("allocator returning something too small to be a valid AllocatorDefinition")
161 { 175 {
162 // a function that provides something that is too small to contain an AllocatorDefinition, should cause an error 176 // a function that provides something that attempts to pass as an AllocatorDefinition, but is not, should cause an error
163 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) { 177 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) {
164 // create a full userdata that is too small (it only contains enough to store a version tag, but not the rest 178 // create a full userdata of the correct size, but of course the contents don't match
165 auto* const _duck{ static_cast<lanes::AllocatorDefinition::version_t*>(lua_newuserdata(L_, sizeof(lanes::AllocatorDefinition::version_t))) }; 179 int* const _duck{ static_cast<int*>(lua_newuserdata(L_, sizeof(lanes::AllocatorDefinition))) };
166 *_duck = 666777; 180 _duck[0] = 666;
167 return 1; 181 _duck[1] = 777;
168 }; 182 return 1;
169 lua_pushcfunction(L, _provideAllocator); 183 };
170 lua_setglobal(L, "ProvideAllocator"); 184 lua_pushcfunction(L, _provideAllocator);
171 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected 185 lua_setglobal(L, "ProvideAllocator");
172 // which would prevent us from calling _provideAllocator 186 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected
173 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}"); 187 // which would prevent us from calling _provideAllocator
174 } 188 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}");
189 }
190}
175 191
176 // ----------------------------------------------------------------------------------------- 192// #################################################################################################
177 193
178 SECTION("allocator returning something too small to be a valid AllocatorDefinition") 194TEST_CASE("lanes.configure.internal_allocator")
179 { 195{
180 // a function that provides something that attempts to pass as an AllocatorDefinition, but is not, should cause an error 196 LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
181 static constexpr lua_CFunction _provideAllocator = +[](lua_State* const L_) {
182 // create a full userdata of the correct size, but of course the contents don't match
183 int* const _duck{ static_cast<int*>(lua_newuserdata(L_, sizeof(lanes::AllocatorDefinition))) };
184 _duck[0] = 666;
185 _duck[1] = 777;
186 return 1;
187 };
188 lua_pushcfunction(L, _provideAllocator);
189 lua_setglobal(L, "ProvideAllocator");
190 // force value of internal_allocator so that the LuaJIT-default 'libc' is not selected
191 // which would prevent us from calling _provideAllocator
192 L.requireFailure("require 'lanes'.configure{allocator = ProvideAllocator, internal_allocator = 'allocator'}");
193 }
194 }
195 197
196 // ---------------------------------------------------------------------------------------------
197 // internal_allocator should be a string, "libc"/"allocator" 198 // internal_allocator should be a string, "libc"/"allocator"
198 199
199 SECTION("[internal_allocator") 200 SECTION("internal_allocator = false")
200 { 201 {
201 SECTION("internal_allocator = false") 202 L.requireFailure("require 'lanes'.configure{internal_allocator = false}");
202 { 203 }
203 L.requireFailure("require 'lanes'.configure{internal_allocator = false}");
204 }
205 204
206 // ----------------------------------------------------------------------------------------- 205 // ---------------------------------------------------------------------------------------------
207 206
208 SECTION("internal_allocator = true") 207 SECTION("internal_allocator = true")
209 { 208 {
210 L.requireFailure("require 'lanes'.configure{internal_allocator = true}"); 209 L.requireFailure("require 'lanes'.configure{internal_allocator = true}");
211 } 210 }
212 211
213 // ----------------------------------------------------------------------------------------- 212 // ---------------------------------------------------------------------------------------------
214 213
215 SECTION("internal_allocator = <table>") 214 SECTION("internal_allocator = <table>")
216 { 215 {
217 L.requireFailure("require 'lanes'.configure{internal_allocator = {}}"); 216 L.requireFailure("require 'lanes'.configure{internal_allocator = {}}");
218 } 217 }
219 218
220 // ----------------------------------------------------------------------------------------- 219 // ---------------------------------------------------------------------------------------------
221 220
222 SECTION("internal_allocator = <Lua function>") 221 SECTION("internal_allocator = <Lua function>")
223 { 222 {
224 L.requireFailure("require 'lanes'.configure{internal_allocator = function() end}"); 223 L.requireFailure("require 'lanes'.configure{internal_allocator = function() end}");
225 } 224 }
226 225
227 // ----------------------------------------------------------------------------------------- 226 // ---------------------------------------------------------------------------------------------
228 227
229 SECTION("internal_allocator = <bad string>") 228 SECTION("internal_allocator = <bad string>")
230 { 229 {
231 L.requireFailure("require 'lanes'.configure{internal_allocator = 'gluh'}"); 230 L.requireFailure("require 'lanes'.configure{internal_allocator = 'gluh'}");
232 } 231 }
233 232
234 // ----------------------------------------------------------------------------------------- 233 // ---------------------------------------------------------------------------------------------
235 234
236 SECTION("internal_allocator = 'libc'") 235 SECTION("internal_allocator = 'libc'")
237 { 236 {
238 L.requireSuccess("require 'lanes'.configure{internal_allocator = 'libc'}"); 237 L.requireSuccess("require 'lanes'.configure{internal_allocator = 'libc'}");
239 } 238 }
240 239
241 // ----------------------------------------------------------------------------------------- 240 // ---------------------------------------------------------------------------------------------
242 241
243 SECTION("internal_allocator = 'allocator'") 242 SECTION("internal_allocator = 'allocator'")
244 { 243 {
245 L.requireSuccess("require 'lanes'.configure{internal_allocator = 'allocator'}"); 244 L.requireSuccess("require 'lanes'.configure{internal_allocator = 'allocator'}");
246 }
247 } 245 }
246}
247
248// #################################################################################################
249
250TEST_CASE("lanes.configure.keepers_gc_threshold")
251{
252 LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
248 253
249 // ---------------------------------------------------------------------------------------------
250 // keepers_gc_threshold should be a number in [0, 100] 254 // keepers_gc_threshold should be a number in [0, 100]
251 255
252 SECTION("keepers_gc_threshold") 256 SECTION("keepers_gc_threshold = <table>")
253 { 257 {
254 SECTION("keepers_gc_threshold = <table>") 258 L.requireFailure("require 'lanes'.configure{keepers_gc_threshold = {}}");
255 { 259 }
256 L.requireFailure("require 'lanes'.configure{keepers_gc_threshold = {}}");
257 }
258 260
259 // ----------------------------------------------------------------------------------------- 261 // ---------------------------------------------------------------------------------------------
260 262
261 SECTION("keepers_gc_threshold = <string>") 263 SECTION("keepers_gc_threshold = <string>")
262 { 264 {
263 L.requireFailure("require 'lanes'.configure{keepers_gc_threshold = 'gluh'}"); 265 L.requireFailure("require 'lanes'.configure{keepers_gc_threshold = 'gluh'}");
264 } 266 }
265 267
266 // ----------------------------------------------------------------------------------------- 268 // ---------------------------------------------------------------------------------------------
267 269
268 SECTION("keepers_gc_threshold = -1") 270 SECTION("keepers_gc_threshold = -1")
269 { 271 {
270 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = -1}"); 272 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = -1}");
271 } 273 }
272 274
273 // ----------------------------------------------------------------------------------------- 275 // ---------------------------------------------------------------------------------------------
274 276
275 SECTION("keepers_gc_threshold = 0") 277 SECTION("keepers_gc_threshold = 0")
276 { 278 {
277 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = 0}"); 279 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = 0}");
278 } 280 }
279 281
280 // ----------------------------------------------------------------------------------------- 282 // ---------------------------------------------------------------------------------------------
281 283
282 SECTION("keepers_gc_threshold = 100") 284 SECTION("keepers_gc_threshold = 100")
283 { 285 {
284 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = 100}"); 286 L.requireSuccess("require 'lanes'.configure{keepers_gc_threshold = 100}");
285 }
286 } 287 }
288}
289
290// #################################################################################################
291
292TEST_CASE("lanes.configure.the rest")
293{
294 LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
295
287 296
288 // --------------------------------------------------------------------------------------------- 297 // ---------------------------------------------------------------------------------------------
289 // nb_user_keepers should be a number in [0, 100] 298 // nb_user_keepers should be a number in [0, 100]