Skip to content

Commit 75e1a45

Browse files
committed
fix(UI): make heightmap presenter thread safe [closes #87]
1 parent e5a6f55 commit 75e1a45

File tree

2 files changed

+55
-34
lines changed

2 files changed

+55
-34
lines changed

src/UI/Screens/Heightmap/HeightmapPresenter.cpp

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111

1212
namespace UI
1313
{
14-
/**
15-
* FIXME: #87 This class is not thread safe since m_heightmap can be accessed and set by multiple threads without
16-
* synchronization. This can cause the program to crash if for example the Duet disconnects while a heightmap is
17-
* being rendered.
18-
*/
19-
2014
void HeightmapPresenter::setRenderMode(HeightmapRenderMode mode)
2115
{
2216
ZoneScoped;
@@ -27,8 +21,11 @@ namespace UI
2721
void HeightmapPresenter::setHeightmap(const std::shared_ptr<OM::Heightmap>& heightmap)
2822
{
2923
ZoneScoped;
30-
m_heightmap = heightmap;
31-
if (m_heightmap == nullptr)
24+
{
25+
std::unique_lock lock(m_heightmapMutex);
26+
m_heightmap = heightmap;
27+
}
28+
if (heightmap == nullptr)
3229
{
3330
m_view->clear();
3431
}
@@ -42,31 +39,37 @@ namespace UI
4239
return;
4340
}
4441

45-
if (m_heightmap == nullptr)
42+
std::shared_ptr<OM::Heightmap> heightmap;
43+
{
44+
std::shared_lock lock(m_heightmapMutex);
45+
heightmap = m_heightmap;
46+
}
47+
48+
if (heightmap == nullptr)
4649
{
4750
m_view->clear();
4851
return;
4952
}
5053

51-
if (!m_heightmap->IsValid())
54+
if (!heightmap->IsValid())
5255
{
5356
LOG_WARN("Heightmap is not valid");
5457
m_view->clear();
5558
return;
5659
}
5760

58-
auto axis0 = m_heightmap->meta.GetAxis(0);
59-
auto axis1 = m_heightmap->meta.GetAxis(1);
61+
auto axis0 = heightmap->meta.GetAxis(0);
62+
auto axis1 = heightmap->meta.GetAxis(1);
6063

6164
if (axis0 == nullptr || axis1 == nullptr)
6265
{
6366
LOG_WARN("Heightmap axes are not valid");
6467
return;
6568
}
6669

67-
LOG_DBG("Rendering heightmap {:s}", m_heightmap->GetFileName());
70+
LOG_DBG("Rendering heightmap {:s}", heightmap->GetFileName());
6871

69-
m_view->setShownHeightmapName(m_heightmap->GetFileName());
72+
m_view->setShownHeightmapName(heightmap->GetFileName());
7073
m_view->setXRange({static_cast<int32_t>(axis0->minPosition), static_cast<int32_t>(axis0->maxPosition)});
7174
m_view->setYRange({static_cast<int32_t>(axis1->minPosition), static_cast<int32_t>(axis1->maxPosition)});
7275

@@ -79,8 +82,8 @@ namespace UI
7982
m_view->setValueRange(-0.25f, 0.25f);
8083
break;
8184
case HeightmapRenderMode::Auto:
82-
m_view->setValueRange(static_cast<float>(m_heightmap->GetMinError()),
83-
static_cast<float>(m_heightmap->GetMaxError()));
85+
m_view->setValueRange(static_cast<float>(heightmap->GetMinError()),
86+
static_cast<float>(heightmap->GetMaxError()));
8487
break;
8588
}
8689

@@ -113,7 +116,7 @@ namespace UI
113116
{
114117
float x = x_min + (static_cast<float>(px) * xStep);
115118
float y = y_min + (static_cast<float>(py) * yStep);
116-
double value = m_heightmap->GetInterpolatedPoint(x, y);
119+
double value = heightmap->GetInterpolatedPoint(x, y);
117120
if (std::isnan(value))
118121
{
119122
continue;
@@ -126,20 +129,20 @@ namespace UI
126129
m_view->renderColorBar();
127130

128131
#if RENDER_MEASUREMENT_POINTS
129-
const auto& measurements = m_heightmap->GetPoints();
132+
const auto& measurements = heightmap->GetPoints();
130133
for (size_t i = 0; i < measurements.size(); i++)
131134
{
132135
const auto& point = measurements[i];
133136
m_view->addMeasurementPoint(point.x, point.y);
134137
}
135138
#endif
136139

137-
m_view->setStatistics(m_heightmap->GetPointCount(),
138-
m_heightmap->GetArea() / 100,
139-
m_heightmap->GetMinError(),
140-
m_heightmap->GetMaxError(),
141-
m_heightmap->GetMeanError(),
142-
m_heightmap->GetStdDev());
140+
m_view->setStatistics(heightmap->GetPointCount(),
141+
heightmap->GetArea() / 100,
142+
heightmap->GetMinError(),
143+
heightmap->GetMaxError(),
144+
heightmap->GetMeanError(),
145+
heightmap->GetStdDev());
143146
}
144147

145148
void HeightmapPresenter::setActiveHeightmap(const size_t index)
@@ -153,8 +156,15 @@ namespace UI
153156

154157
const std::string& name = m_heightmapFiles[index]->GetName();
155158
LOG_INFO("Loading heightmap {:s}", name);
156-
m_heightmap = OM::GetHeightmapData(name);
157-
m_heightmap->LoadFromDuet([this](OM::Heightmap& /* heightmap */) { render(); });
159+
auto map = OM::GetHeightmapData(name);
160+
{
161+
std::unique_lock lock(m_heightmapMutex);
162+
m_heightmap = map;
163+
}
164+
if (map)
165+
{
166+
map->LoadFromDuet([this](OM::Heightmap& /* heightmap */) { render(); });
167+
}
158168
}
159169

160170
void HeightmapPresenter::toggleHeightmap(const size_t index)
@@ -212,14 +222,19 @@ namespace UI
212222
{
213223
ZoneScoped;
214224
LOG_DBG("New axes data");
215-
if (m_heightmap == nullptr)
225+
std::shared_ptr<OM::Heightmap> heightmap;
226+
{
227+
std::shared_lock lock(m_heightmapMutex);
228+
heightmap = m_heightmap;
229+
}
230+
if (heightmap == nullptr)
216231
{
217232
LOG_DBG("Heightmap is not set, skipping axis range update");
218233
return;
219234
}
220235

221-
auto axis0 = m_heightmap->meta.GetAxis(0);
222-
auto axis1 = m_heightmap->meta.GetAxis(1);
236+
auto axis0 = heightmap->meta.GetAxis(0);
237+
auto axis1 = heightmap->meta.GetAxis(1);
223238

224239
if (axis0 == nullptr || axis1 == nullptr)
225240
{
@@ -234,7 +249,7 @@ namespace UI
234249
return;
235250
}
236251

237-
if (m_heightmap != nullptr && m_heightmap->IsValid())
252+
if (heightmap != nullptr && heightmap->IsValid())
238253
{
239254
render();
240255
}
@@ -323,11 +338,14 @@ namespace UI
323338
{
324339
ZoneScoped;
325340
LOG_DBG("Disconnect");
326-
if (m_heightmap != nullptr)
327341
{
328-
m_heightmap = nullptr;
329-
m_view->clear();
330-
m_view->setHeightmapCount(0);
342+
std::unique_lock lock(m_heightmapMutex);
343+
if (m_heightmap != nullptr)
344+
{
345+
m_heightmap = nullptr;
346+
}
331347
}
348+
m_view->clear();
349+
m_view->setHeightmapCount(0);
332350
}
333351
} // namespace UI

src/UI/Screens/Heightmap/HeightmapPresenter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "ObjectModel/Heightmap.h"
44
#include "UI/Core/Presenter.h"
5+
#include "tracy/Tracy.hpp"
6+
#include <shared_mutex>
57

68
namespace UI
79
{
@@ -58,6 +60,7 @@ namespace UI
5860
bool checkMode();
5961

6062
std::shared_ptr<OM::Heightmap> m_heightmap;
63+
TracySharedLockable(std::shared_mutex, m_heightmapMutex);
6164
OM::FileSystem::ItemList m_heightmapFiles;
6265
HeightmapRenderMode m_mode = HeightmapRenderMode::Fixed;
6366
AxisRange m_axis0Range;

0 commit comments

Comments
 (0)