Skip to content

Commit 36378a4

Browse files
committed
Fix bug in dataflow <-> GUI progress communication (race condition, thread blocking)
1 parent 2ceca97 commit 36378a4

8 files changed

Lines changed: 97 additions & 47 deletions

File tree

plugins/dataflow_analysis/include/dataflow_analysis/utils/progress_printer.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ namespace hal
3636
public:
3737
ProgressPrinter(u32 max_message_size = 0);
3838

39-
void print_progress(float progress, const std::string& message = "");
39+
void print_progress_to_stderr(float progress, const std::string& message = "");
40+
41+
// GUI calls must not be called from semaphore/mutex protected scope, otherwise threads might block each other
42+
void print_progress_to_gui(int percent = -1); // use m_last_percentage if negative
43+
44+
void print_message_to_gui(const std::string& message);
4045

4146
void clear();
4247

@@ -48,8 +53,10 @@ namespace hal
4853

4954
u32 m_printed_progress;
5055
std::string m_last_message;
56+
std::string m_gui_message; // not all messages are relevant for GUI
5157
u32 m_bar_width;
5258
u32 m_max_message_size;
59+
int m_last_percentage;
5360
int m_terminal_width; // no terminal found if negative
5461
};
5562
} // namespace dataflow

plugins/dataflow_analysis/src/evaluation/evaluation.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,11 @@ namespace hal
128128
// scan groups until all or done
129129
float original_size = sorted_results.size();
130130
ProgressPrinter progress_bar;
131+
progress_bar.print_message_to_gui("dataflow: evaluate results …");
131132
while (!sorted_results.empty())
132133
{
133-
progress_bar.print_progress((original_size - sorted_results.size()) / original_size);
134+
progress_bar.print_progress_to_stderr((original_size - sorted_results.size()) / original_size);
135+
progress_bar.print_progress_to_gui();
134136

135137
// precompute the group indices of each gate
136138
std::unordered_map<u32, u32> max_group_size_of_gate;

plugins/dataflow_analysis/src/pre_processing/pre_processing.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ namespace hal
8888
measure_block_time("identifying successors and predecessors of sequential gates") ProgressPrinter progress_bar;
8989
float cnt = 0;
9090

91+
progress_bar.print_message_to_gui("dataflow: preprocessing …");
92+
9193
// cache map of nets to group indices of known net groups
9294
std::unordered_map<const Net*, u32> net_to_group_index;
9395
for (u32 i = 0; i < config.known_net_groups.size(); i++)
@@ -110,7 +112,8 @@ namespace hal
110112
for (const auto& gate : netlist_abstr.target_gates)
111113
{
112114
cnt++;
113-
progress_bar.print_progress(cnt / netlist_abstr.target_gates.size());
115+
progress_bar.print_progress_to_stderr(cnt / netlist_abstr.target_gates.size());
116+
progress_bar.print_progress_to_gui();
114117

115118
// create sets even if there are no successors
116119
if (netlist_abstr.gate_to_successors.find(gate->get_id()) == netlist_abstr.gate_to_successors.end())

plugins/dataflow_analysis/src/pre_processing/register_stage_identification.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ namespace hal
5353
for (const auto& sequential_gate : netlist_abstr.target_gates)
5454
{
5555
cnt++;
56-
progress_bar.print_progress(cnt / netlist_abstr.target_gates.size());
56+
progress_bar.print_progress_to_stderr(cnt / netlist_abstr.target_gates.size());
5757

5858
auto current = sequential_gate->get_id();
5959

@@ -140,7 +140,7 @@ namespace hal
140140
for (u32 i = 0; i < stages_to_split; ++i)
141141
{
142142
++cnt;
143-
progress_bar.print_progress(cnt / stages_to_split);
143+
progress_bar.print_progress_to_stderr(cnt / stages_to_split);
144144

145145
std::unordered_map<u32, std::vector<u32>> move_out_reasons;
146146
for (auto g : ctx.stages[i])
@@ -238,7 +238,7 @@ namespace hal
238238
for (const auto& stage : directional_stages[i].stages)
239239
{
240240
cnt++;
241-
progress_bar.print_progress(cnt / (directional_stages[0].stages.size() + directional_stages[1].stages.size()));
241+
progress_bar.print_progress_to_stderr(cnt / (directional_stages[0].stages.size() + directional_stages[1].stages.size()));
242242
for (auto it = directional_stages[1 - i].stages.begin(); it != directional_stages[1 - i].stages.end();)
243243
{
244244
auto& other_stage = *it;
@@ -296,7 +296,7 @@ namespace hal
296296
std::set<std::vector<u32>> seen;
297297
for (auto& [g, stages] : netlist_abstr.gate_to_register_stages)
298298
{
299-
progress_bar.print_progress(++cnt / netlist_abstr.gate_to_register_stages.size());
299+
progress_bar.print_progress_to_stderr(++cnt / netlist_abstr.gate_to_register_stages.size());
300300

301301
if (stages.size() > 1)
302302
{
@@ -336,7 +336,7 @@ namespace hal
336336
cnt = 0;
337337
for (const auto& combine : spread_stages)
338338
{
339-
progress_bar.print_progress(++cnt / netlist_abstr.gate_to_register_stages.size());
339+
progress_bar.print_progress_to_stderr(++cnt / netlist_abstr.gate_to_register_stages.size());
340340

341341
for (auto stage_id : combine)
342342
{

plugins/dataflow_analysis/src/processing/processing.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ namespace hal
6262
ctx.pass_counter = end_id;
6363
if (ctx.pass_counter >= num_passes)
6464
{
65+
std::lock_guard guard(ctx.result_mutex);
6566
ctx.done = true;
6667
}
6768
}
@@ -72,13 +73,16 @@ namespace hal
7273

7374
if (auto it = ctx.pass_outcome.find({current_state, current_pass.id}); it != ctx.pass_outcome.end())
7475
{
75-
// early exit, outcome is already known
76-
std::lock_guard guard(ctx.result_mutex);
77-
ctx.new_recurring_results.emplace_back(current_state, current_pass.id, it->second);
78-
ctx.finished_passes++;
79-
m_progress_printer.print_progress((float)ctx.finished_passes / ctx.current_passes.size(),
76+
{
77+
// early exit, outcome is already known
78+
std::lock_guard guard(ctx.result_mutex);
79+
ctx.new_recurring_results.emplace_back(current_state, current_pass.id, it->second);
80+
ctx.finished_passes++;
81+
m_progress_printer.print_progress_to_stderr((float)ctx.finished_passes / ctx.current_passes.size(),
8082
std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
8183
+ std::to_string(ctx.new_unique_groupings.size()) + " new results)");
84+
}
85+
m_progress_printer.print_progress_to_gui();
8286
continue;
8387
}
8488

@@ -107,10 +111,11 @@ namespace hal
107111
}
108112

109113
ctx.finished_passes++;
110-
m_progress_printer.print_progress((float)ctx.finished_passes / ctx.current_passes.size(),
114+
m_progress_printer.print_progress_to_stderr((float)ctx.finished_passes / ctx.current_passes.size(),
111115
std::to_string(ctx.finished_passes) + "\\" + std::to_string(ctx.current_passes.size()) + " ("
112116
+ std::to_string(ctx.new_unique_groupings.size()) + " new results)");
113117
}
118+
m_progress_printer.print_progress_to_gui();
114119
}
115120
}
116121
}

plugins/dataflow_analysis/src/utils/progress_printer.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ namespace hal
1313
{
1414
namespace dataflow
1515
{
16-
ProgressPrinter::ProgressPrinter(u32 max_message_size) : m_bar_width(0), m_max_message_size(max_message_size), m_terminal_width(get_terminal_width())
16+
ProgressPrinter::ProgressPrinter(u32 max_message_size) : m_gui_message("dataflow analysis …"),
17+
m_bar_width(0), m_max_message_size(max_message_size),
18+
m_last_percentage(0), m_terminal_width(get_terminal_width())
1719
{
1820
if (m_terminal_width <= 8)
1921
{
@@ -28,15 +30,28 @@ namespace hal
2830
reset();
2931
}
3032

31-
void ProgressPrinter::print_progress(float progress, const std::string& message)
33+
void ProgressPrinter::print_message_to_gui(const std::string& message)
3234
{
33-
progress = std::clamp(progress, 0.f, 1.f);
34-
u32 int_progress = (u32)(progress * 100.0f);
35-
if (GuiExtensionDataflow::s_progress_indicator_function)
35+
m_gui_message = message;
36+
print_progress_to_gui();
37+
}
38+
39+
void ProgressPrinter::print_progress_to_gui(int percent)
40+
{
41+
if (!GuiExtensionDataflow::s_progress_indicator_function) return;
42+
if (percent < 0) percent = m_last_percentage;
43+
if (percent > 99) percent = 99;
3644
{
37-
GuiExtensionDataflow::s_progress_indicator_function(int_progress < 100 ? int_progress : 99, "dataflow analysis running ...");
45+
GuiExtensionDataflow::s_progress_indicator_function(percent, m_gui_message);
3846
}
3947

48+
}
49+
50+
void ProgressPrinter::print_progress_to_stderr(float progress, const std::string& message)
51+
{
52+
progress = std::clamp(progress, 0.f, 1.f);
53+
m_last_percentage = (u32)(progress * 100.0f);
54+
4055
if (m_terminal_width <= 8)
4156
{
4257
return;
@@ -82,7 +97,7 @@ namespace hal
8297
std::stringstream str;
8398
str << "[" << bar << "] ";
8499

85-
str << std::right << std::setw(3) << int_progress << '%';
100+
str << std::right << std::setw(3) << m_last_percentage << '%';
86101

87102
if (!print_message.empty())
88103
{

plugins/gui/include/gui/graph_widget/progress_bar.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <QPushButton>
3232
#include <QImage>
3333
#include <QTimer>
34+
#include <QMutex>
3435

3536
namespace hal {
3637

@@ -64,12 +65,14 @@ namespace hal {
6465
Q_OBJECT
6566
QImage mImage;
6667
double mAngle;
68+
QMutex* mMutex;
6769
public Q_SLOTS:
6870
void handleTimeout();
6971
protected:
7072
void paintEvent(QPaintEvent* event) override;
7173
public:
7274
BusyAnimation(QWidget* parent = nullptr);
75+
~BusyAnimation();
7376
};
7477

7578
class BusyIndicator : public AbstractBusyIndicator
@@ -78,6 +81,7 @@ namespace hal {
7881
QTimer* mTimer;
7982
QLabel* mLabel;
8083
QProgressBar* mProgressBar;
84+
QMutex* mMutex;
8185
public:
8286
BusyIndicator(QWidget* parent = nullptr);
8387
~BusyIndicator();

plugins/gui/src/graph_widget/progress_bar.cpp

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ namespace hal {
4646
BusyIndicator::BusyIndicator(QWidget* parent)
4747
: AbstractBusyIndicator(parent)
4848
{
49+
mMutex = new QMutex;
4950
QVBoxLayout* layout = new QVBoxLayout(this);
5051
layout->setAlignment(Qt::AlignHCenter);
5152
layout->addWidget(mLabel = new QLabel(this));
@@ -60,12 +61,18 @@ namespace hal {
6061
BusyIndicator::~BusyIndicator()
6162
{
6263
mTimer->stop();
64+
delete mMutex;
6365
}
6466

6567
BusyAnimation::BusyAnimation(QWidget* parent)
66-
: QWidget(parent), mImage(QImage(":/icons/hal9000","PNG")), mAngle(0)
68+
: QWidget(parent), mImage(QImage(":/icons/hal9000","PNG")), mAngle(0), mMutex(new QMutex)
6769
{;}
6870

71+
BusyAnimation::~BusyAnimation()
72+
{
73+
delete mMutex;
74+
}
75+
6976
void BusyAnimation::handleTimeout()
7077
{
7178
mAngle += 5;
@@ -76,50 +83,57 @@ namespace hal {
7683
void BusyAnimation::paintEvent(QPaintEvent* event)
7784
{
7885
Q_UNUSED(event);
79-
QPainter p(this);
80-
p.setRenderHint(QPainter::Antialiasing);
86+
if (mMutex->try_lock())
87+
{
88+
QPainter p(this);
89+
p.setRenderHint(QPainter::Antialiasing);
8190

82-
int rw = rect().width();
83-
int rh = rect().height();
91+
int rw = rect().width();
92+
int rh = rect().height();
8493

85-
int w = rw > rh ? rh : rw;
86-
int h = w;
94+
int w = rw > rh ? rh : rw;
95+
int h = w;
8796

88-
QRect rimg((rw-w)/2,(rh-h)/2,w,h);
97+
QRect rimg((rw-w)/2,(rh-h)/2,w,h);
8998

90-
// p.fillRect(rimg,QBrush(Qt::gray));
91-
QImage img = mImage.scaled(w,h);
92-
double xc = w / 2.;
93-
double yc = h / 2.;
99+
// p.fillRect(rimg,QBrush(Qt::gray));
100+
QImage img = mImage.scaled(w,h);
101+
double xc = w / 2.;
102+
double yc = h / 2.;
94103

95-
for (int y = 0; y<h; y++)
96-
for (int x=0; x<w; x++)
97-
{
98-
QRgb col = img.pixel(x,y);
99-
if (col & 0xFF000000)
104+
for (int y = 0; y<h; y++)
105+
for (int x=0; x<w; x++)
100106
{
101-
double angle = atan2(y-yc,x-xc) / M_PI * 180.;
102-
double da = angle >= mAngle ? angle - mAngle : angle + 360. - mAngle;
103-
int opaque = floor(da / 360.*256);
104-
opaque <<= 24;
105-
col = (col & 0xFFFFFF) | opaque;
106-
img.setPixel(x,y,col);
107+
QRgb col = img.pixel(x,y);
108+
if (col & 0xFF000000)
109+
{
110+
double angle = atan2(y-yc,x-xc) / M_PI * 180.;
111+
double da = angle >= mAngle ? angle - mAngle : angle + 360. - mAngle;
112+
int opaque = floor(da / 360.*256);
113+
opaque <<= 24;
114+
col = (col & 0xFFFFFF) | opaque;
115+
img.setPixel(x,y,col);
116+
}
107117
}
108-
}
109118

110-
p.drawImage(rimg,img);
119+
p.drawImage(rimg,img);
120+
mMutex->unlock();
121+
}
111122
}
112123

113124
void BusyIndicator::setValue(int percent)
114125
{
126+
if (mProgressBar->value() == percent) return; // nothing to do
127+
QMutexLocker lock(mMutex);
115128
mProgressBar->setValue(percent);
116129
update();
117130
qApp->processEvents();
118131
}
119132

120133
void BusyIndicator::setText(const QString &txt)
121134
{
122-
if (mLabel->text() == txt) return;
135+
if (mLabel->text() == txt) return; // nothing to do
136+
QMutexLocker lock(mMutex);
123137
mLabel->setText(txt);
124138
qApp->processEvents();
125139
}

0 commit comments

Comments
 (0)