#define SOL_ALL_SAFETIES_ON 1 #include #include #include #include #include #include #include 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> 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 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> 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 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 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>; if constexpr (std::is_same_v) { std::cout << "received a double: " << returned_data << "\n"; } else if constexpr (std::is_same_v>) { std::cout << "received a std::vector: { "; for (std::size_t i = 0; i < returned_data.size(); ++i) { std::cout << returned_data[i]; if (i != static_cast( 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; }