Skip to content

Commit 7246e0a

Browse files
committed
Fixed thread safety and added more tests
Now creation of Updater is thread safe. Added synchronization for evaluatedJsSources set. Added more unit tests.
1 parent 49f0171 commit 7246e0a

4 files changed

Lines changed: 191 additions & 4 deletions

File tree

include/AdblockPlus/Platform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ namespace AdblockPlus
141141
std::shared_future<FilterEnginePtr> filterEngine;
142142
std::shared_ptr<Updater> updater;
143143
std::set<std::string> evaluatedJsSources;
144+
std::mutex evaluatedJsSourcesMutex;
144145
std::function<void(const std::string&)> GetEvaluateCallback();
145146
};
146147

libadblockplus.gyp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@
209209
'test/Prefs.cpp',
210210
'test/ReferrerMapping.cpp',
211211
'test/UpdateCheck.cpp',
212-
'test/WebRequest.cpp'
212+
'test/WebRequest.cpp',
213+
'test/UpdaterAndFilterEngineCreation.cpp'
213214
],
214215
'msvs_settings': {
215216
'VCLinkerTool': {

src/Platform.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ std::function<void(const std::string&)> Platform::GetEvaluateCallback()
7272
// GetEvaluateCallback() method assumes that jsEngine is already created
7373
return [this](const std::string& filename)
7474
{
75+
std::lock_guard<std::mutex> lock(evaluatedJsSourcesMutex);
7576
if (evaluatedJsSources.find(filename) != evaluatedJsSources.end())
7677
return; //NO-OP, file was already evaluated
7778

@@ -119,12 +120,18 @@ FilterEngine& Platform::GetFilterEngine()
119120

120121
Updater& Platform::GetUpdater()
121122
{
122-
if (updater == nullptr)
123123
{
124-
GetJsEngine(); // ensures that JsEngine is instantiated
124+
std::lock_guard<std::mutex> lock(modulesMutex);
125+
if (updater)
126+
return *updater;
127+
}
128+
GetJsEngine(); // ensures that JsEngine is instantiated
129+
{
130+
std::lock_guard<std::mutex> lock(modulesMutex);
131+
if (!updater)
125132
updater = std::make_shared<Updater>(jsEngine, GetEvaluateCallback());
133+
return *updater;
126134
}
127-
return *updater;
128135
}
129136

130137
void Platform::WithTimer(const WithTimerCallback& callback)
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* This file is part of Adblock Plus <https://adblockplus.org/>,
3+
* Copyright (C) 2006-present eyeo GmbH
4+
*
5+
* Adblock Plus is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 3 as
7+
* published by the Free Software Foundation.
8+
*
9+
* Adblock Plus is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
19+
#include <thread>
20+
#include <vector>
21+
#include <chrono>
22+
#include <string>
23+
24+
#include "BaseJsTest.h"
25+
26+
using namespace AdblockPlus;
27+
28+
namespace
29+
{
30+
class UpdaterAndFilterEngineCreationTest : public BaseJsTest
31+
{
32+
protected:
33+
34+
const size_t COUNT = 1000;
35+
const std::string PROP_NAME = "patternsbackupinterval";
36+
std::vector<std::thread> threadsVector;
37+
std::vector<Updater*> updaterAddrVector;
38+
std::mutex updaterAddrVectorMutex;
39+
std::vector<FilterEngine*> filterAddrVector;
40+
std::mutex filterAddrVectorMutex;
41+
DelayedWebRequest::SharedTasks webRequestTasks;
42+
DelayedTimer::SharedTasks timerTasks;
43+
44+
void SetUp()
45+
{
46+
LazyFileSystem* fileSystem;
47+
ThrowingPlatformCreationParameters platformParams;
48+
platformParams.logSystem.reset(new LazyLogSystem());
49+
platformParams.timer = DelayedTimer::New(timerTasks);
50+
platformParams.fileSystem.reset(fileSystem = new LazyFileSystem());
51+
platformParams.webRequest = DelayedWebRequest::New(webRequestTasks);
52+
platform.reset(new Platform(std::move(platformParams)));
53+
threadsVector.reserve(COUNT);
54+
updaterAddrVector.reserve(COUNT);
55+
filterAddrVector.reserve(COUNT);
56+
}
57+
58+
void CallGetUpdaterSimultaneously()
59+
{
60+
CallGetSimultaneously(true, false);
61+
}
62+
63+
void CallGetFilterEngineSimultaneously()
64+
{
65+
CallGetSimultaneously(false, true);
66+
}
67+
68+
void CallGetUpdaterAndGetFilterEngineSimultaneously()
69+
{
70+
CallGetSimultaneously(true, true);
71+
}
72+
73+
private:
74+
75+
void CallGetSimultaneously(bool isUpdater, bool isFilterEngine)
76+
{
77+
for (size_t idx = 0; idx < COUNT; ++idx)
78+
threadsVector.push_back(
79+
std::thread([this, idx, isUpdater, isFilterEngine]() -> void {
80+
Updater* updaterAddr = nullptr;
81+
FilterEngine* filterEngineAddr = nullptr;
82+
if (isUpdater && isFilterEngine)
83+
{
84+
// some randomization in order of calling gets
85+
if (idx % 2)
86+
{
87+
updaterAddr = &(platform->GetUpdater());
88+
filterEngineAddr = &(platform->GetFilterEngine());
89+
}
90+
else
91+
{
92+
filterEngineAddr = &(platform->GetFilterEngine());
93+
updaterAddr = &(platform->GetUpdater());
94+
}
95+
}
96+
else if (isUpdater)
97+
updaterAddr = &(platform->GetUpdater());
98+
else if (isFilterEngine)
99+
filterEngineAddr = &(platform->GetFilterEngine());
100+
101+
if (updaterAddr != nullptr)
102+
{
103+
std::lock_guard<std::mutex> lock(updaterAddrVectorMutex);
104+
updaterAddrVector[idx] = updaterAddr;
105+
}
106+
if (filterEngineAddr != nullptr)
107+
{
108+
std::lock_guard<std::mutex> lock(filterAddrVectorMutex);
109+
filterAddrVector[idx] = filterEngineAddr;
110+
}
111+
}));
112+
113+
// join the threads
114+
for (auto& th: threadsVector)
115+
if (th.joinable())
116+
th.join();
117+
}
118+
};
119+
}
120+
121+
TEST_F(UpdaterAndFilterEngineCreationTest, TestFilterEngineSingleInstance)
122+
{
123+
CallGetFilterEngineSimultaneously();
124+
FilterEngine* filterEngineAddr = filterAddrVector[0];
125+
for (size_t i = 1; i < COUNT; ++i)
126+
ASSERT_EQ(filterEngineAddr, filterAddrVector[i]);
127+
}
128+
129+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterSingleInstance)
130+
{
131+
CallGetUpdaterSimultaneously();
132+
Updater* updaterAddr = updaterAddrVector[0];
133+
for (size_t i = 1; i < COUNT; ++i)
134+
ASSERT_EQ(updaterAddr, updaterAddrVector[i]);
135+
}
136+
137+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationsDontCollide)
138+
{
139+
CallGetUpdaterAndGetFilterEngineSimultaneously();
140+
ASSERT_EQ(filterAddrVector[0], filterAddrVector[COUNT - 1]);
141+
ASSERT_EQ(updaterAddrVector[0], updaterAddrVector[COUNT - 1]);
142+
}
143+
144+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder1)
145+
{
146+
Updater& updater = platform->GetUpdater();
147+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
148+
FilterEngine& filterEngine = platform->GetFilterEngine();
149+
150+
int propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
151+
int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
152+
ASSERT_EQ(propFromUpdater, propFromFilterEngine);
153+
154+
int newPropValue = 8;
155+
updater.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue));
156+
propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
157+
propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
158+
ASSERT_EQ(propFromUpdater, newPropValue);
159+
ASSERT_EQ(propFromFilterEngine, newPropValue);
160+
}
161+
162+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder2)
163+
{
164+
FilterEngine& filterEngine = platform->GetFilterEngine();
165+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
166+
Updater& updater = platform->GetUpdater();
167+
168+
int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
169+
int propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
170+
ASSERT_EQ(propFromUpdater, propFromFilterEngine);
171+
172+
int newPropValue = 18;
173+
filterEngine.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue));
174+
propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
175+
propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
176+
ASSERT_EQ(propFromUpdater, newPropValue);
177+
ASSERT_EQ(propFromFilterEngine, newPropValue);
178+
}

0 commit comments

Comments
 (0)