158 lines
3.9 KiB
C++
158 lines
3.9 KiB
C++
|
#define SOL_ALL_SAFETIES_ON 1
|
||
|
#include <sol/sol.hpp>
|
||
|
|
||
|
#include <mutex>
|
||
|
#include <condition_variable>
|
||
|
#include <thread>
|
||
|
#include <variant>
|
||
|
#include <cstddef>
|
||
|
#include <iostream>
|
||
|
|
||
|
struct worker_data {
|
||
|
std::mutex until_ready_mutex;
|
||
|
std::condition_variable until_ready_condition;
|
||
|
bool is_ready = false;
|
||
|
bool is_processed = false;
|
||
|
sol::state worker_lua;
|
||
|
sol::bytecode payload;
|
||
|
std::variant<double, std::vector<double>> return_payload;
|
||
|
|
||
|
worker_data() {
|
||
|
worker_lua.open_libraries(sol::lib::base);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void worker_thread(worker_data& data) {
|
||
|
for ([[maybe_unused]] std::uint64_t loops = 0; true; ++loops) {
|
||
|
// Wait until main() sends data
|
||
|
std::unique_lock<std::mutex> data_lock(
|
||
|
data.until_ready_mutex);
|
||
|
data.until_ready_condition.wait(
|
||
|
data_lock, [&data] { return data.is_ready; });
|
||
|
|
||
|
if (data.payload.size() == 0) {
|
||
|
// signaling we are done
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// just for easier typing
|
||
|
sol::state& lua = data.worker_lua;
|
||
|
|
||
|
// we own the lock now, do the work
|
||
|
std::variant<double, std::vector<double>> result
|
||
|
= lua.safe_script(data.payload.as_string_view());
|
||
|
|
||
|
// store returning payload,
|
||
|
// clear current payload
|
||
|
data.return_payload = std::move(result);
|
||
|
data.payload.clear();
|
||
|
|
||
|
// Send result back to main
|
||
|
std::cout << "worker_thread data processing is "
|
||
|
"completed: signaling & unlocking\n";
|
||
|
data.is_processed = true;
|
||
|
data.is_ready = false;
|
||
|
data_lock.unlock();
|
||
|
data.until_ready_condition.notify_one();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main() {
|
||
|
|
||
|
// main lua state
|
||
|
sol::state lua;
|
||
|
lua.open_libraries(sol::lib::base);
|
||
|
|
||
|
// set up functions, etc. etc.
|
||
|
lua.script("function f () return 4.5 end");
|
||
|
lua.script("function g () return { 1.1, 2.2, 3.3 } end");
|
||
|
|
||
|
// kick off worker
|
||
|
worker_data data;
|
||
|
std::thread worker(worker_thread, std::ref(data));
|
||
|
|
||
|
// main Lua state
|
||
|
bool done_working = false;
|
||
|
for (std::uint64_t loops = 0; !done_working; ++loops) {
|
||
|
// finished working? send nothing
|
||
|
// even loop? use f
|
||
|
// otherwise, use g
|
||
|
if (loops >= 3) {
|
||
|
data.payload.clear();
|
||
|
done_working = true;
|
||
|
}
|
||
|
else if ((loops % 2) == 0) {
|
||
|
sol::function target = lua["f"];
|
||
|
data.payload = target.dump();
|
||
|
}
|
||
|
else {
|
||
|
sol::function target = lua["g"];
|
||
|
data.payload = target.dump();
|
||
|
}
|
||
|
|
||
|
// send data to the worker thread
|
||
|
{
|
||
|
std::lock_guard<std::mutex> lk(
|
||
|
data.until_ready_mutex);
|
||
|
data.is_ready = true;
|
||
|
std::cout
|
||
|
<< "function serialized: sending to worker "
|
||
|
"thread to execute on Lua state...\n";
|
||
|
}
|
||
|
data.until_ready_condition.notify_one();
|
||
|
|
||
|
if (done_working) {
|
||
|
break;
|
||
|
}
|
||
|
// wait for the worker
|
||
|
{
|
||
|
std::unique_lock<std::mutex>
|
||
|
lock_waiting_for_worker(
|
||
|
data.until_ready_mutex);
|
||
|
data.until_ready_condition.wait(
|
||
|
lock_waiting_for_worker,
|
||
|
[&data] { return data.is_processed; });
|
||
|
data.is_processed = false;
|
||
|
}
|
||
|
auto data_processor = [](auto& returned_data) {
|
||
|
using option_type
|
||
|
= std::remove_cv_t<std::remove_reference_t<
|
||
|
decltype(returned_data)>>;
|
||
|
if constexpr (std::is_same_v<option_type,
|
||
|
double>) {
|
||
|
std::cout << "received a double: "
|
||
|
<< returned_data << "\n";
|
||
|
}
|
||
|
else if constexpr (std::is_same_v<option_type,
|
||
|
std::vector<double>>) {
|
||
|
std::cout
|
||
|
<< "received a std::vector<double>: { ";
|
||
|
for (std::size_t i = 0;
|
||
|
i < returned_data.size();
|
||
|
++i) {
|
||
|
std::cout << returned_data[i];
|
||
|
if (i
|
||
|
!= static_cast<std::size_t>(
|
||
|
returned_data.size() - 1)) {
|
||
|
std::cout << ", ";
|
||
|
}
|
||
|
}
|
||
|
std::cout << " }\n";
|
||
|
}
|
||
|
else {
|
||
|
std::cerr << "OH MY GOD YOU FORGOT TO "
|
||
|
"HANDLE A TYPE OF DATA FROM A "
|
||
|
"WORKER ABORT ABORT ABORT\n";
|
||
|
std::abort();
|
||
|
}
|
||
|
};
|
||
|
std::visit(data_processor, data.return_payload);
|
||
|
}
|
||
|
|
||
|
// join and wait for workers to come back
|
||
|
worker.join();
|
||
|
|
||
|
// workers are back, exit program
|
||
|
return 0;
|
||
|
}
|