Skip to content

Instantly share code, notes, and snippets.

@cleoold
Created September 3, 2020 00:08
Show Gist options
  • Save cleoold/6a9a5f96316b9bcb27a684dc9846a7e8 to your computer and use it in GitHub Desktop.
Save cleoold/6a9a5f96316b9bcb27a684dc9846a7e8 to your computer and use it in GitHub Desktop.
demo: open 3 Lua states and let them interact. Two of them run infinite loops and the other notifies them via c function to stop looping
// g++ test.cpp -lpthread -llua5.3 -Wall
#include <array>
#include <atomic>
#include <chrono>
#include <future>
#include <iostream>
#include "lua.hpp"
namespace {
auto luacode1 = R"(
for i = 1, t_nVMs-1 do
t_sleep(1)
t_notify_other_vm(i)
end
)";
auto luacode2 = R"(
while not t_should_stop() do
io.write(('* thread %d running...\n'):format(t_id))
t_sleep(0.5)
end
io.write(('- thread %d done\n'):format(t_id))
)";
constexpr auto nVMs = 3; // adjustable
std::array<lua_State *, nVMs> luas;
std::array<std::atomic_bool, nVMs> should_stops;
int l_sleep(lua_State *L) {
auto secs = luaL_checknumber(L, 1);
std::this_thread::sleep_for(std::chrono::milliseconds{static_cast<int64_t>(secs*1000)});
return 0;
}
int l_notify_other_vm(lua_State *L) {
auto theirid = luaL_checkinteger(L, 1);
should_stops[theirid] = true;
return 0;
}
int l_should_stop(lua_State *L) {
lua_getglobal(L, "t_id");
auto myid = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_pushboolean(L, should_stops[myid]);
return 1;
}
}
int main() {
// init lua states
for (auto i = 0; i < nVMs; ++i) {
auto &l = luas[i];
if (!(l = luaL_newstate())) {
std::cerr << i << ": cannot create lua state" << std::endl;
exit(1);
}
// register constants
luaL_openlibs(l);
lua_register(l, "t_sleep", l_sleep);
lua_pushinteger(l, i);
lua_setglobal(l, "t_id");
// init flags
should_stops[i] = false;
// set notifier function for first vm
if (i == 0) {
lua_register(l, "t_notify_other_vm", l_notify_other_vm);
lua_pushinteger(l, nVMs);
lua_setglobal(l, "t_nVMs");
// set receiver function for other vms
} else {
lua_register(l, "t_should_stop", l_should_stop);
}
}
auto tasks = std::array<std::future<void>, nVMs>{};
auto ready_promises = std::array<std::promise<void>, nVMs>{};
auto ready_futures = std::array<std::future<void>, nVMs>{};
auto fire_promise = std::promise<void>{};
auto fire_future = fire_promise.get_future().share();
for (auto i = 0; i < nVMs; ++i)
ready_futures[i] = ready_promises[i].get_future();
// send lua vms
for (auto i = 0; i < nVMs; ++i) {
tasks[i] = std::async(std::launch::async,
[i, fire_future, &l = luas[i], &ready_promise = ready_promises[i],
&code = i == 0 ? luacode1 : luacode2] {
if (luaL_loadstring(l, code)) {
std::cerr << i << ": loadstring error " << lua_tostring(l, -1) << std::endl;
exit(1);
}
ready_promise.set_value();
// wait for all vms
fire_future.wait();
// run the codes
if (lua_pcall(l, 0, 0, 0))
std::cerr << i << ": error " << lua_tostring(l, -1) << std::endl;
});
}
// wait for vms (threads) to finish initializing then start all vms
for (auto &f : ready_futures) f.wait();
fire_promise.set_value();
// join threads
for (auto &f : tasks) f.wait();
// close lua states
for (auto &l : luas) lua_close(l);
}
* thread 1 running...
* thread 2 running...
* thread 2 running...
* thread 1 running...
* thread 2 running...
- thread 1 done
* thread 2 running...
- thread 2 done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment