diff options
Diffstat (limited to '')
-rw-r--r-- | tests/LuaThreadStress/LuaThreadStress.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/tests/LuaThreadStress/LuaThreadStress.cpp b/tests/LuaThreadStress/LuaThreadStress.cpp new file mode 100644 index 000000000..f4d782ddc --- /dev/null +++ b/tests/LuaThreadStress/LuaThreadStress.cpp @@ -0,0 +1,154 @@ +// LuaThreadStress.cpp + +// Implements a stress-test of cLuaState under several threads + +#include "Globals.h" +#include "Bindings/LuaState.h" +#include <thread> +#include <random> + + + + + +/** How long the threading test should run. */ +static const int NUM_SECONDS_TO_TEST = 10; + + + + + +/** Retrieves a callback from the Lua state that can be later called. +Calls the Lua function getCallback with a_Seed param to retrieve the callback. */ +static cLuaState::cCallbackPtr getCallback(cLuaState & a_LuaState, unsigned a_Seed) +{ + cLuaState::cLock lock(a_LuaState); + cLuaState::cCallbackPtr res; + a_LuaState.Call("getCallback", a_Seed, cLuaState::Return, res); + return res; +} + + + + + +/** Runs a single thread that stress-tests the cLuaState object. +a_LuaState is the Lua state on which to operate. +a_Seed is the seed for the random number generator for this thread. +a_ShouldTerminate is a bool flag that another thread sets to ask this thread to terminate. +a_FailResult is a shared result state that is written by any thread upon failure (so if it contains nonzero, at least one thread has failed). */ +static void runStress(cLuaState * a_LuaState, unsigned a_Seed, std::atomic<bool> * a_ShouldTerminate, std::atomic<int> * a_FailResult) +{ + std::minstd_rand rnd; + rnd.seed(a_Seed); + auto callbackSeed = static_cast<unsigned>(rnd()); + auto callback = getCallback(*a_LuaState, callbackSeed); + while (!a_ShouldTerminate->load()) + { + // Pick a random operation on the Lua state and peform it: + switch (rnd() % 4) + { + case 0: + { + // Get a new callback: + callbackSeed = callbackSeed + 1; + callback = getCallback(*a_LuaState, callbackSeed); + break; + } + + default: + { + // Call the callback, if still available: + auto param = static_cast<unsigned>(rnd()); + unsigned returnValue; + if (callback->Call(param, cLuaState::Return, returnValue)) + { + if (returnValue != param + callbackSeed) + { + LOGWARNING("Bad value returned from the callback"); + *a_FailResult = 2; + a_ShouldTerminate->store(true); + return; + } + } + break; + } + } // switch (random) + + // Once in every ~10k operations, reload the lua state completely: + if ((rnd() % 10000) == 0) + { + cLuaState::cLock lock(*a_LuaState); + a_LuaState->Close(); + a_LuaState->Create(); + if (!a_LuaState->LoadFile("Test.lua")) + { + *a_FailResult = 3; + a_ShouldTerminate->store(true); + return; + } + } + } // while (!a_ShouldTerminate) +} + + + + + +static int DoTest(void) +{ + cLuaState L("LuaThreadStress test"); + L.Create(); + if (!L.LoadFile("Test.lua")) + { + return 1; + } + + // Start the concurrect threads: + std::atomic<bool> shouldTerminate(false); + std::atomic<int> failResult(0); + std::thread threads[] = + { + std::thread(runStress, &L, 0, &shouldTerminate, &failResult), + std::thread(runStress, &L, 1, &shouldTerminate, &failResult), + std::thread(runStress, &L, 2, &shouldTerminate, &failResult), + std::thread(runStress, &L, 3, &shouldTerminate, &failResult), + }; + + // Let the threads run wild: + for (int i = 1; i <= NUM_SECONDS_TO_TEST; ++i) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + LOG("Testing (%d out of %d seconds)...", i, NUM_SECONDS_TO_TEST); + } + + // Terminate everything: + LOG("Terminating the threads"); + shouldTerminate = true; + for (auto & t: threads) + { + t.join(); + } + LOG("Threads terminated."); + + return failResult.load(); +} + + + + + +int main() +{ + LOG("LuaThreadStress starting."); + + int res = DoTest(); + LOG("LuaThreadStress test done: %s", (res == 0) ? "success" : "failure"); + if (res != 0) + { + return res; + } + + LOG("LuaThreadStress finished."); + return 0; +} |