/**
* SexprEval.cc
*
* S-expression evaluator.
*
* Copyright (c) 2008, 2014, 2015, 2020 Linas Vepstas
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <chrono>
#include <thread>
#include <opencog/util/Logger.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/persist/sexcom/Dispatcher.h>
#include "SexprEval.h"
using namespace opencog;
// Singleton shared instance holding single shared frame cache.
Dispatcher SexprEval::_interpreter;
SexprEval::SexprEval(const AtomSpacePtr& asp)
: GenericEval()
{
_atomspace = asp;
_interpreter.set_base_space(asp);
}
SexprEval::~SexprEval()
{
}
/* ============================================================== */
/**
* Evaluate an s-expresion.
*/
void SexprEval::eval_expr(const std::string &expr)
{
_caught_error = false;
try {
std::lock_guard<std::mutex> lock(_mtx);
_answer = _interpreter.interpret_command(expr);
// CogStorageNode expects all responses to be terminated
// by exactly one newline char. It is the end-of-message
// marker. Empty responses have no newline - the client
// should not call recv() for commands that return nothing.
if (not _answer.empty())
_answer += "\n";
}
catch (const StandardException& ex)
{
_error_string = ex.what();
_caught_error = true;
}
catch (const std::runtime_error& ex)
{
_error_string = ex.what();
_caught_error = true;
}
}
std::string SexprEval::poll_result()
{
std::string ret;
std::lock_guard<std::mutex> lock(_mtx);
ret.swap(_answer);
return ret;
}
void SexprEval::begin_eval()
{
std::unique_lock<std::mutex> lock(_mtx);
if (_answer.empty()) return;
// Unusual race condition: some other thread is using this evaluator,
// and evaluation there has not completed. This seems to be rare,
// and I think we can just ignore this, at least for a little while.
auto start = std::chrono::steady_clock::now();
bool warned = false;
while (not _answer.empty())
{
// Release lock and yield so poll_result() can clear _answer
lock.unlock();
std::this_thread::yield();
lock.lock();
auto elapsed = std::chrono::steady_clock::now() - start;
auto secs = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
// Only warn if we've been waiting a long time (> 1 second)
if (not warned and secs >= 1)
{
logger().warn("SexprEval::begin_eval: Buffer not empty after 1 sec, size=%lu",
_answer.size());
warned = true;
}
// Give up after 60 seconds - don't clear, something is very wrong
if (secs >= 60)
{
logger().error("SexprEval::begin_eval: Giving up after 60 sec, size=%lu",
_answer.size());
return;
}
}
}
/* ============================================================== */
/**
* interrupt() - convert user's control-C at keyboard into exception.
*/
void SexprEval::interrupt(void)
{
_caught_error = true;
_error_string = "Caught interrupt!";
}
SexprEval* SexprEval::get_evaluator(const AtomSpacePtr& asp)
{
static thread_local SexprEval* evaluator = new SexprEval(asp);
// The eval_dtor runs when this thread is destroyed.
class eval_dtor {
public:
~eval_dtor() { delete evaluator; }
};
static thread_local eval_dtor killer;
return evaluator;
}
/* ===================== END OF FILE ======================== */