multithreaded singleton WolframLink class
This commit is contained in:
171
src/WolframLink.cpp
Normal file
171
src/WolframLink.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "WolframLink.hpp"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Static member definitions
|
||||
// ----------------------------------------------------------------
|
||||
std::shared_ptr<WolframLink> WolframLink::instance_ = nullptr;
|
||||
std::mutex WolframLink::instance_mutex_;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Singleton
|
||||
// ----------------------------------------------------------------
|
||||
std::shared_ptr<WolframLink> WolframLink::instance(WSLINK link) {
|
||||
std::lock_guard<std::mutex> lock(instance_mutex_);
|
||||
if (!instance_) {
|
||||
if (!link) {
|
||||
throw std::runtime_error("WolframLink: first call must provide a WSLINK");
|
||||
}
|
||||
instance_ = std::shared_ptr<WolframLink>(new WolframLink(link));
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void WolframLink::destroy() {
|
||||
std::lock_guard<std::mutex> lock(instance_mutex_);
|
||||
instance_.reset();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Constructor / Destructor
|
||||
// ----------------------------------------------------------------
|
||||
WolframLink::WolframLink(WSLINK link)
|
||||
: link_(link)
|
||||
, shutdown_(false)
|
||||
, worker_([this]() { worker_loop(); })
|
||||
{
|
||||
spdlog::debug("WolframLink created, worker started");
|
||||
}
|
||||
|
||||
WolframLink::~WolframLink() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
shutdown_ = true;
|
||||
}
|
||||
queue_cv_.notify_all();
|
||||
if (worker_.joinable()) {
|
||||
worker_.join();
|
||||
}
|
||||
spdlog::debug("WolframLink destroyed, worker joined");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Public API
|
||||
// ----------------------------------------------------------------
|
||||
std::future<std::optional<std::string>> WolframLink::evaluate(const std::string& expr) {
|
||||
auto promise = std::make_shared<std::promise<std::optional<std::string>>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
if (shutdown_) {
|
||||
promise->set_value(std::nullopt);
|
||||
return future;
|
||||
}
|
||||
queue_.push([this, expr, promise]() {
|
||||
promise->set_value(evaluate_on_worker(expr));
|
||||
});
|
||||
}
|
||||
queue_cv_.notify_one();
|
||||
return future;
|
||||
}
|
||||
|
||||
std::future<std::optional<std::string>> WolframLink::evaluate_and_log(
|
||||
const std::string& label,
|
||||
const std::string& expr)
|
||||
{
|
||||
auto promise = std::make_shared<std::promise<std::optional<std::string>>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
if (shutdown_) {
|
||||
promise->set_value(std::nullopt);
|
||||
return future;
|
||||
}
|
||||
queue_.push([this, label, expr, promise]() {
|
||||
auto result = evaluate_on_worker(expr);
|
||||
if (result) {
|
||||
spdlog::info("[{}]\n expr => {}\n result => {}\n",
|
||||
label, expr, *result);
|
||||
} else {
|
||||
spdlog::error("[{}] Failed to evaluate: {}", label, expr);
|
||||
}
|
||||
promise->set_value(result);
|
||||
});
|
||||
}
|
||||
queue_cv_.notify_one();
|
||||
return future;
|
||||
}
|
||||
|
||||
std::size_t WolframLink::queue_depth() const {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return queue_.size();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Worker thread
|
||||
// ----------------------------------------------------------------
|
||||
void WolframLink::worker_loop() {
|
||||
while (true) {
|
||||
std::function<void()> job;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
queue_cv_.wait(lock, [this]() {
|
||||
return !queue_.empty() || shutdown_;
|
||||
});
|
||||
|
||||
if (shutdown_ && queue_.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
job = std::move(queue_.front());
|
||||
queue_.pop();
|
||||
}
|
||||
|
||||
try {
|
||||
job();
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::error("WolframLink worker caught exception: {}", e.what());
|
||||
} catch (...) {
|
||||
spdlog::error("WolframLink worker caught unknown exception");
|
||||
}
|
||||
}
|
||||
spdlog::debug("WolframLink worker loop exiting");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// WSTP I/O — only ever called from worker_loop()
|
||||
// ----------------------------------------------------------------
|
||||
std::optional<std::string> WolframLink::evaluate_on_worker(const std::string& expr) {
|
||||
std::string wrapped = "ToString[" + expr + ", InputForm]";
|
||||
|
||||
WSPutFunction(link_, "EvaluatePacket", 1);
|
||||
WSPutFunction(link_, "ToExpression", 1);
|
||||
WSPutString(link_, wrapped.c_str());
|
||||
WSEndPacket(link_);
|
||||
WSFlush(link_);
|
||||
|
||||
int pkt;
|
||||
while ((pkt = WSNextPacket(link_)) != RETURNPKT) {
|
||||
if (pkt == 0) {
|
||||
spdlog::error("WSNextPacket returned 0 (link error): {}",
|
||||
WSErrorMessage(link_));
|
||||
return std::nullopt;
|
||||
}
|
||||
WSNewPacket(link_);
|
||||
}
|
||||
|
||||
const char* result = nullptr;
|
||||
if (!WSGetString(link_, &result)) {
|
||||
spdlog::error("WSGetString failed: {}", WSErrorMessage(link_));
|
||||
WSNewPacket(link_);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string out(result);
|
||||
WSReleaseString(link_, result);
|
||||
WSNewPacket(link_);
|
||||
return out;
|
||||
}
|
||||
68
src/WolframLink.hpp
Normal file
68
src/WolframLink.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <wstp.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
class WolframLink {
|
||||
public:
|
||||
// ----------------------------------------------------------------
|
||||
// Singleton access
|
||||
// ----------------------------------------------------------------
|
||||
static std::shared_ptr<WolframLink> instance(WSLINK link = nullptr);
|
||||
static void destroy();
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Non-copyable / non-movable
|
||||
// ----------------------------------------------------------------
|
||||
WolframLink(const WolframLink&) = delete;
|
||||
WolframLink& operator=(const WolframLink&) = delete;
|
||||
WolframLink(WolframLink&&) = delete;
|
||||
WolframLink& operator=(WolframLink&&) = delete;
|
||||
|
||||
~WolframLink();
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Public API — callable from any thread
|
||||
// ----------------------------------------------------------------
|
||||
std::future<std::optional<std::string>> evaluate(const std::string& expr);
|
||||
|
||||
std::future<std::optional<std::string>> evaluate_and_log(
|
||||
const std::string& label,
|
||||
const std::string& expr);
|
||||
|
||||
std::size_t queue_depth() const;
|
||||
|
||||
private:
|
||||
explicit WolframLink(WSLINK link);
|
||||
|
||||
void worker_loop();
|
||||
std::optional<std::string> evaluate_on_worker(const std::string& expr);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Members
|
||||
// ----------------------------------------------------------------
|
||||
WSLINK link_;
|
||||
std::atomic<bool> shutdown_;
|
||||
|
||||
mutable std::mutex queue_mutex_;
|
||||
std::condition_variable queue_cv_;
|
||||
std::queue<std::function<void()>> queue_;
|
||||
|
||||
std::thread worker_;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Singleton state
|
||||
// ----------------------------------------------------------------
|
||||
static std::shared_ptr<WolframLink> instance_;
|
||||
static std::mutex instance_mutex_;
|
||||
};
|
||||
Reference in New Issue
Block a user