Skip to content

Commit 20804a4

Browse files
Arsentiy MilchakovSergey Yershov
Arsentiy Milchakov
authored and
Sergey Yershov
committed
notes fix
1 parent 3a7ba19 commit 20804a4

File tree

3 files changed

+69
-62
lines changed

3 files changed

+69
-62
lines changed

editor/editor_notes.cpp

+43-36
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
#include "base/string_utils.hpp"
1212
#include "base/timer.hpp"
1313

14-
#include "std/chrono.hpp"
15-
#include "std/future.hpp"
14+
#include <chrono>
15+
#include <future>
1616

1717
#include "3party/pugixml/src/pugixml.hpp"
1818

1919
namespace
2020
{
21-
bool LoadFromXml(pugi::xml_document const & xml, list<editor::Note> & notes,
21+
bool LoadFromXml(pugi::xml_document const & xml, std::list<editor::Note> & notes,
2222
uint32_t & uploadedNotesCount)
2323
{
2424
uint64_t notesCount;
@@ -55,7 +55,7 @@ bool LoadFromXml(pugi::xml_document const & xml, list<editor::Note> & notes,
5555
return true;
5656
}
5757

58-
void SaveToXml(list<editor::Note> const & notes, pugi::xml_document & xml,
58+
void SaveToXml(std::list<editor::Note> const & notes, pugi::xml_document & xml,
5959
uint32_t const uploadedNotesCount)
6060
{
6161
auto constexpr kDigitsAfterComma = 7;
@@ -74,9 +74,10 @@ void SaveToXml(list<editor::Note> const & notes, pugi::xml_document & xml,
7474
}
7575

7676
/// Not thread-safe, use only for initialization.
77-
bool Load(string const & fileName, list<editor::Note> & notes, uint32_t & uploadedNotesCount)
77+
bool Load(std::string const & fileName, std::list<editor::Note> & notes,
78+
uint32_t & uploadedNotesCount)
7879
{
79-
string content;
80+
std::string content;
8081
try
8182
{
8283
auto const reader = GetPlatform().GetReader(fileName);
@@ -111,30 +112,31 @@ bool Load(string const & fileName, list<editor::Note> & notes, uint32_t & upload
111112
}
112113

113114
/// Not thread-safe, use synchronization.
114-
bool Save(string const & fileName, list<editor::Note> const & notes,
115+
bool Save(std::string const & fileName, std::list<editor::Note> const & notes,
115116
uint32_t const uploadedNotesCount)
116117
{
117118
pugi::xml_document xml;
118119
SaveToXml(notes, xml, uploadedNotesCount);
119-
return my::WriteToTempAndRenameToFile(
120-
fileName, [&xml](string const & fileName) { return xml.save_file(fileName.data(), " "); });
120+
return my::WriteToTempAndRenameToFile(fileName, [&xml](std::string const & fileName) {
121+
return xml.save_file(fileName.data(), " ");
122+
});
121123
}
122124
} // namespace
123125

124126
namespace editor
125127
{
126-
shared_ptr<Notes> Notes::MakeNotes(string const & fileName, bool const fullPath)
128+
std::shared_ptr<Notes> Notes::MakeNotes(std::string const & fileName, bool const fullPath)
127129
{
128-
return shared_ptr<Notes>(
130+
return std::shared_ptr<Notes>(
129131
new Notes(fullPath ? fileName : GetPlatform().WritablePathForFile(fileName)));
130132
}
131133

132-
Notes::Notes(string const & fileName) : m_fileName(fileName)
134+
Notes::Notes(std::string const & fileName) : m_fileName(fileName)
133135
{
134136
Load(m_fileName, m_notes, m_uploadedNotesCount);
135137
}
136138

137-
void Notes::CreateNote(ms::LatLon const & latLon, string const & text)
139+
void Notes::CreateNote(ms::LatLon const & latLon, std::string const & text)
138140
{
139141
if (text.empty())
140142
{
@@ -148,7 +150,14 @@ void Notes::CreateNote(ms::LatLon const & latLon, string const & text)
148150
return;
149151
}
150152

151-
lock_guard<mutex> g(m_mu);
153+
std::lock_guard<std::mutex> g(m_mu);
154+
auto const it = std::find_if(m_notes.begin(), m_notes.end(), [&latLon, &text](Note const & note) {
155+
return latLon.EqualDxDy(note.m_point, kTolerance) && text == note.m_note;
156+
});
157+
// No need to add the same note. It works in case when saved note are not uploaded yet.
158+
if (it != m_notes.end())
159+
return;
160+
152161
m_notes.emplace_back(latLon, text);
153162
Save(m_fileName, m_notes, m_uploadedNotesCount);
154163
}
@@ -159,20 +168,19 @@ void Notes::Upload(osm::OsmOAuth const & auth)
159168
auto const self = shared_from_this();
160169

161170
auto const doUpload = [self, auth]() {
162-
size_t size;
163-
164-
{
165-
lock_guard<mutex> g(self->m_mu);
166-
size = self->m_notes.size();
167-
}
168-
171+
std::unique_lock<std::mutex> ulock(self->m_mu);
172+
// Size of m_notes is decreased only in this method.
173+
size_t size = notes.size();
174+
auto & notes = self->m_notes;
169175
osm::ServerApi06 api(auth);
170-
auto it = begin(self->m_notes);
171-
for (size_t i = 0; i != size; ++i, ++it)
176+
177+
while (size > 0)
172178
{
173179
try
174180
{
175-
auto const id = api.CreateNote(it->m_point, it->m_note);
181+
ulock.unlock();
182+
auto const id = api.CreateNote(notes.front().m_point, notes.front().m_note);
183+
ulock.lock();
176184
LOG(LINFO, ("A note uploaded with id", id));
177185
}
178186
catch (osm::ServerApi06::ServerApi06Exception const & e)
@@ -182,35 +190,34 @@ void Notes::Upload(osm::OsmOAuth const & auth)
182190
return;
183191
}
184192

185-
lock_guard<mutex> g(self->m_mu);
186-
it = self->m_notes.erase(it);
193+
notes.pop_front();
194+
--size;
187195
++self->m_uploadedNotesCount;
188196
Save(self->m_fileName, self->m_notes, self->m_uploadedNotesCount);
189197
}
190198
};
191199

192-
// Do not run more than one upload thread at a time.
193-
static auto future = async(launch::async, doUpload);
194-
auto const status = future.wait_for(milliseconds(0));
195-
if (status == future_status::ready)
196-
future = async(launch::async, doUpload);
200+
static auto future = std::async(std::launch::async, doUpload);
201+
auto const status = future.wait_for(std::chrono::milliseconds(0));
202+
if (status == std::future_status::ready)
203+
future = std::async(std::launch::async, doUpload);
197204
}
198205

199-
vector<Note> const Notes::GetNotes() const
206+
std::list<Note> Notes::GetNotes() const
200207
{
201-
lock_guard<mutex> g(m_mu);
202-
return {begin(m_notes), end(m_notes)};
208+
std::lock_guard<std::mutex> g(m_mu);
209+
return m_notes;
203210
}
204211

205212
size_t Notes::NotUploadedNotesCount() const
206213
{
207-
lock_guard<mutex> g(m_mu);
214+
std::lock_guard<std::mutex> g(m_mu);
208215
return m_notes.size();
209216
}
210217

211218
size_t Notes::UploadedNotesCount() const
212219
{
213-
lock_guard<mutex> g(m_mu);
220+
std::lock_guard<std::mutex> g(m_mu);
214221
return m_uploadedNotesCount;
215222
}
216223
} // namespace editor

editor/editor_notes.hpp

+17-15
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,52 @@
66

77
#include "base/macros.hpp"
88

9-
#include "std/list.hpp"
10-
#include "std/mutex.hpp"
11-
#include "std/shared_ptr.hpp"
12-
#include "std/string.hpp"
9+
#include <list>
10+
#include <memory>
11+
#include <mutex>
12+
#include <string>
1313

1414
namespace editor
1515
{
1616
struct Note
1717
{
18-
Note(ms::LatLon const & point, string const & text) : m_point(point), m_note(text) {}
18+
Note(ms::LatLon const & point, std::string const & text) : m_point(point), m_note(text) {}
1919
ms::LatLon m_point;
20-
string m_note;
20+
std::string m_note;
2121
};
2222

2323
inline bool operator==(Note const & a, Note const & b)
2424
{
2525
return a.m_point == b.m_point && b.m_note == b.m_note;
2626
}
2727

28-
class Notes : public enable_shared_from_this<Notes>
28+
class Notes : public std::enable_shared_from_this<Notes>
2929
{
3030
public:
31-
static shared_ptr<Notes> MakeNotes(string const & fileName = "notes.xml",
32-
bool const fullPath = false);
31+
static float constexpr kTolerance = 1e-7;
32+
static std::shared_ptr<Notes> MakeNotes(std::string const & fileName = "notes.xml",
33+
bool const fullPath = false);
3334

34-
void CreateNote(ms::LatLon const & latLon, string const & text);
35+
void CreateNote(ms::LatLon const & latLon, std::string const & text);
3536

3637
/// Uploads notes to the server in a separate thread.
38+
/// Called on main thread from system event.
3739
void Upload(osm::OsmOAuth const & auth);
3840

39-
vector<Note> const GetNotes() const;
41+
std::list<Note> GetNotes() const;
4042

4143
size_t NotUploadedNotesCount() const;
4244
size_t UploadedNotesCount() const;
4345

4446
private:
45-
Notes(string const & fileName);
47+
explicit Notes(std::string const & fileName);
4648

47-
string const m_fileName;
48-
mutable mutex m_mu;
49+
std::string const m_fileName;
50+
mutable std::mutex m_mu;
4951

5052
// m_notes keeps the notes that have not been uploaded yet.
5153
// Once a note has been uploaded, it is removed from m_notes.
52-
list<Note> m_notes;
54+
std::list<Note> m_notes;
5355

5456
uint32_t m_uploadedNotesCount = 0;
5557

editor/editor_tests/editor_notes_test.cpp

+9-11
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,14 @@ UNIT_TEST(Notes_Smoke)
2727
auto const notes = Notes::MakeNotes(fullFileName, true);
2828
auto const result = notes->GetNotes();
2929
TEST_EQUAL(result.size(), 3, ());
30-
vector<Note> const expected {
31-
{MercatorBounds::ToLatLon({1, 2}), "Some note1"},
32-
{MercatorBounds::ToLatLon({2, 2}), "Some note2"},
33-
{MercatorBounds::ToLatLon({1, 1}), "Some note3"}
34-
};
35-
for (auto i = 0; i < result.size(); ++i)
36-
{
37-
TEST(my::AlmostEqualAbs(result[i].m_point.lat, expected[i].m_point.lat, 1e-7), ());
38-
TEST(my::AlmostEqualAbs(result[i].m_point.lon, expected[i].m_point.lon, 1e-7), ());
39-
TEST_EQUAL(result[i].m_note, expected[i].m_note, ());
40-
}
30+
std::vector<Note> const expected{{MercatorBounds::ToLatLon({1, 2}), "Some note1"},
31+
{MercatorBounds::ToLatLon({2, 2}), "Some note2"},
32+
{MercatorBounds::ToLatLon({1, 1}), "Some note3"}};
33+
34+
auto const isEqual = std::equal(
35+
result.begin(), result.end(), expected.begin(), [](Note const & lhs, Note const & rhs) {
36+
return lhs.m_point.EqualDxDy(rhs.m_point, Notes::kTolerance);
37+
});
38+
TEST(isEqual, ());
4139
}
4240
}

0 commit comments

Comments
 (0)