Skip to content

Commit ddd86b5

Browse files
[calc:1.4] Threadsafe and aborting calculations
* libcalculate does not mention any thread safety. lock it. * Use async calculate(…) function and poll query validity to abort unnecessary calculations. This does not only save resources, but is also required such that the locks above dont introduce stuttering.
1 parent 32a71e1 commit ddd86b5

File tree

3 files changed

+42
-16
lines changed

3 files changed

+42
-16
lines changed

calculator_qalculate/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
project(calculator_qalculate VERSION 1.3)
1+
project(calculator_qalculate VERSION 1.4)
22
albert_plugin(
33
NAME Calculator
44
DESCRIPTION "Evaluate math expressions using Qalculate"

calculator_qalculate/src/plugin.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "plugin.h"
77
#include "ui_configwidget.h"
88
#include <QSettings>
9+
#include <QThread>
910
ALBERT_LOGGING_CATEGORY("qalculate")
1011
using namespace albert;
1112
using namespace std;
@@ -18,6 +19,8 @@ const char* CFG_PARSINGMODE = "parsing_mode";
1819
const uint DEF_PARSINGMODE = (int)PARSING_MODE_CONVENTIONAL;
1920
const char* CFG_PRECISION = "precision";
2021
const uint DEF_PRECISION = 16;
22+
const QStringList icon_urls = {"xdg:calc", ":qalculate"};
23+
std::mutex qalculate_mutex;
2124
}
2225

2326
Plugin::Plugin()
@@ -65,6 +68,7 @@ QWidget *Plugin::buildConfigWidget()
6568
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
6669
this, [this](int index){
6770
settings()->setValue(CFG_ANGLEUNIT, index);
71+
std::lock_guard locker(qalculate_mutex);
6872
eo.parse_options.angle_unit = static_cast<AngleUnit>(index);
6973
});
7074

@@ -74,6 +78,7 @@ QWidget *Plugin::buildConfigWidget()
7478
static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
7579
this, [this](int index){
7680
settings()->setValue(CFG_PARSINGMODE, index);
81+
std::lock_guard locker(qalculate_mutex);
7782
eo.parse_options.parsing_mode = static_cast<ParsingMode>(index);
7883
});
7984

@@ -83,6 +88,7 @@ QWidget *Plugin::buildConfigWidget()
8388
static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
8489
this, [this](int value){
8590
settings()->setValue(CFG_PRECISION, value);
91+
std::lock_guard locker(qalculate_mutex);
8692
qalc->setPrecision(value);
8793
});
8894

@@ -97,10 +103,24 @@ vector<RankItem> Plugin::handleGlobalQuery(const GlobalQuery *query) const
97103
if (trimmed.isEmpty())
98104
return results;
99105

106+
std::lock_guard locker(qalculate_mutex);
107+
100108
auto expression = qalc->unlocalizeExpression(query->string().toStdString(), eo.parse_options);
101-
auto mstruct = qalc->calculate(expression, eo);
109+
110+
qalc->startControl();
111+
MathStructure mstruct;
112+
qalc->calculate(&mstruct, expression, 0, eo);
113+
for (; qalc->busy(); QThread::msleep(10))
114+
if (!query->isValid())
115+
qalc->abort();
116+
qalc->stopControl();
117+
118+
if (!query->isValid())
119+
return results;
120+
102121
if (qalc->message()){
103-
qalc->clearMessages();
122+
for (auto msg = qalc->message(); msg; msg = qalc->nextMessage())
123+
DEBG << QString::fromUtf8(qalc->message()->c_message());
104124
return results;
105125
}
106126

@@ -113,7 +133,7 @@ vector<RankItem> Plugin::handleGlobalQuery(const GlobalQuery *query) const
113133
result,
114134
QString("%1esult of %2").arg(mstruct.isApproximate()?"Approximate r":"R", trimmed),
115135
result, // TODO if handler finally knows its trigger change this to fire a triggered query
116-
{"xdg:calc", ":qalculate"},
136+
icon_urls,
117137
{
118138
{
119139
"cpr", "Copy result to clipboard",
@@ -129,35 +149,46 @@ vector<RankItem> Plugin::handleGlobalQuery(const GlobalQuery *query) const
129149
return results;
130150
}
131151

132-
133-
134152
void Plugin::handleTriggerQuery(TriggerQuery *query) const
135153
{
136154
auto trimmed = query->string().trimmed();
137155
if (trimmed.isEmpty())
138156
return;
139157

158+
std::lock_guard locker(qalculate_mutex);
159+
140160
auto eo_ = eo;
141161
eo_.parse_options.functions_enabled = true;
142162
eo_.parse_options.units_enabled = true;
143163
eo_.parse_options.unknowns_enabled = true;
144164

145165
auto expression = qalc->unlocalizeExpression(query->string().toStdString(), eo_.parse_options);
146-
auto mstruct = qalc->calculate(expression, eo_);
166+
167+
qalc->startControl();
168+
MathStructure mstruct;
169+
qalc->calculate(&mstruct, expression, 0, eo_);
170+
for (; qalc->busy(); QThread::msleep(10))
171+
if (!query->isValid())
172+
qalc->abort();
173+
qalc->stopControl();
174+
175+
if (!query->isValid())
176+
return;
177+
147178
QStringList errors;
148179
for (auto msg = qalc->message(); msg; msg = qalc->nextMessage())
149180
errors << QString::fromUtf8(qalc->message()->c_message());
150-
mstruct.format(po);
151181

152182
if (errors.empty()){
183+
mstruct.format(po);
153184
auto result = QString::fromStdString(mstruct.print(po));
154185
query->add(
155186
StandardItem::make(
156187
"qalc-res",
157188
result,
158189
QString("%1esult of %2").arg(mstruct.isApproximate()?"Approximate r":"R", trimmed),
159190
QString("%1%2").arg(query->trigger(), result),
160-
{"xdg:calc", ":qalculate"},
191+
icon_urls,
161192
{
162193
{
163194
"cpr", "Copy result to clipboard",
@@ -176,7 +207,7 @@ void Plugin::handleTriggerQuery(TriggerQuery *query) const
176207
"qalc-err",
177208
"Evaluation error.",
178209
errors.join(" "),
179-
{"xdg:calc", ":qalculate"},
210+
icon_urls,
180211
{{"manual", "Visit documentation", [=](){ openUrl(URL_MANUAL); }}}
181212
)
182213
);

calculator_qalculate/src/plugin.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// Copyright (C) 2023 Manuel Schneider
22

33
#pragma once
4-
#include "albert/plugin.h"
54
#include "albert/extension/queryhandler/globalqueryhandler.h"
5+
#include "albert/plugin.h"
66
#include <QObject>
77
#include <libqalculate/Calculator.h>
88
#include <memory>
@@ -25,8 +25,3 @@ class Plugin : public albert::plugin::ExtensionPlugin<albert::GlobalQueryHandler
2525
EvaluationOptions eo;
2626
PrintOptions po;
2727
};
28-
29-
30-
31-
// void setGroupSeparatorEnabled(bool enabled);
32-
// void setIParserEnabled(bool enabled);

0 commit comments

Comments
 (0)