Skip to content

Commit 98318ef

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 98318ef

File tree

4 files changed

+185
-4
lines changed

4 files changed

+185
-4
lines changed

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: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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+
static const size_t COUNT = 100;
35+
const std::string PROP_NAME = "patternsbackupinterval";
36+
std::vector<std::thread> threadsVector;
37+
Updater* updaterAddrArray[COUNT];
38+
FilterEngine* filterAddrArray[COUNT];
39+
DelayedWebRequest::SharedTasks webRequestTasks;
40+
DelayedTimer::SharedTasks timerTasks;
41+
42+
void SetUp()
43+
{
44+
LazyFileSystem* fileSystem;
45+
ThrowingPlatformCreationParameters platformParams;
46+
platformParams.logSystem.reset(new LazyLogSystem());
47+
platformParams.timer = DelayedTimer::New(timerTasks);
48+
platformParams.fileSystem.reset(fileSystem = new LazyFileSystem());
49+
platformParams.webRequest = DelayedWebRequest::New(webRequestTasks);
50+
platform.reset(new Platform(std::move(platformParams)));
51+
threadsVector.reserve(COUNT);
52+
}
53+
54+
void CallGetUpdaterSimultaneously()
55+
{
56+
CallGetSimultaneously(true, false);
57+
}
58+
59+
void CallGetFilterEngineSimultaneously()
60+
{
61+
CallGetSimultaneously(false, true);
62+
}
63+
64+
void CallGetUpdaterAndGetFilterEngineSimultaneously()
65+
{
66+
CallGetSimultaneously(true, true);
67+
}
68+
69+
private:
70+
71+
void CallGetSimultaneously(bool isUpdater, bool isFilterEngine)
72+
{
73+
for (size_t idx = 0; idx < COUNT; ++idx)
74+
threadsVector.push_back(
75+
std::thread([this, idx, isUpdater, isFilterEngine]() -> void {
76+
Updater* updaterAddr = nullptr;
77+
FilterEngine* filterEngineAddr = nullptr;
78+
if (isUpdater && isFilterEngine)
79+
{
80+
// some randomization in order of calling gets
81+
if (idx % 2)
82+
{
83+
updaterAddr = &(platform->GetUpdater());
84+
filterEngineAddr = &(platform->GetFilterEngine());
85+
}
86+
else
87+
{
88+
filterEngineAddr = &(platform->GetFilterEngine());
89+
updaterAddr = &(platform->GetUpdater());
90+
}
91+
}
92+
else if (isUpdater)
93+
updaterAddr = &(platform->GetUpdater());
94+
else if (isFilterEngine)
95+
filterEngineAddr = &(platform->GetFilterEngine());
96+
97+
if (updaterAddr != nullptr)
98+
{
99+
updaterAddrArray[idx] = updaterAddr;
100+
}
101+
if (filterEngineAddr != nullptr)
102+
{
103+
filterAddrArray[idx] = filterEngineAddr;
104+
}
105+
}));
106+
107+
// join the threads
108+
for (auto& th: threadsVector)
109+
if (th.joinable())
110+
th.join();
111+
}
112+
};
113+
}
114+
115+
TEST_F(UpdaterAndFilterEngineCreationTest, TestFilterEngineSingleInstance)
116+
{
117+
CallGetFilterEngineSimultaneously();
118+
FilterEngine* filterEngineAddr = filterAddrArray[0];
119+
for (size_t i = 1; i < COUNT; ++i)
120+
ASSERT_EQ(filterEngineAddr, filterAddrArray[i]);
121+
}
122+
123+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterSingleInstance)
124+
{
125+
CallGetUpdaterSimultaneously();
126+
Updater* updaterAddr = updaterAddrArray[0];
127+
for (size_t i = 1; i < COUNT; ++i)
128+
ASSERT_EQ(updaterAddr, updaterAddrArray[i]);
129+
}
130+
131+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationsDontCollide)
132+
{
133+
CallGetUpdaterAndGetFilterEngineSimultaneously();
134+
ASSERT_EQ(filterAddrArray[0], filterAddrArray[COUNT - 1]);
135+
ASSERT_EQ(updaterAddrArray[0], updaterAddrArray[COUNT - 1]);
136+
}
137+
138+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder1)
139+
{
140+
Updater& updater = platform->GetUpdater();
141+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
142+
FilterEngine& filterEngine = platform->GetFilterEngine();
143+
144+
int propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
145+
int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
146+
ASSERT_EQ(propFromUpdater, propFromFilterEngine);
147+
148+
int newPropValue = 8;
149+
updater.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue));
150+
propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
151+
propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
152+
ASSERT_EQ(propFromUpdater, newPropValue);
153+
ASSERT_EQ(propFromFilterEngine, newPropValue);
154+
}
155+
156+
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder2)
157+
{
158+
FilterEngine& filterEngine = platform->GetFilterEngine();
159+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
160+
Updater& updater = platform->GetUpdater();
161+
162+
int propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
163+
int propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
164+
ASSERT_EQ(propFromUpdater, propFromFilterEngine);
165+
166+
int newPropValue = 18;
167+
filterEngine.SetPref(PROP_NAME, GetJsEngine().NewValue(newPropValue));
168+
propFromFilterEngine = filterEngine.GetPref(PROP_NAME).AsInt();
169+
propFromUpdater = updater.GetPref(PROP_NAME).AsInt();
170+
ASSERT_EQ(propFromUpdater, newPropValue);
171+
ASSERT_EQ(propFromFilterEngine, newPropValue);
172+
}

0 commit comments

Comments
 (0)