-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring task #6
base: master
Are you sure you want to change the base?
Conversation
Created Updater class for handling application updates and moved respective API from FilterEngine to Updater component. For now Updater class does not care about asynchronicity (if any) in JS files which are used for update checks logic. TODO: Move Updater class to dedicated .h and .cpp files. Abstracted jsSources from FilterEngine. Now every component (like FilterEngine and Updater) created by Platfrom gets EvaluateCallback and evaluates own JS files needed for internal logic. EvaluateCallback keeps track of evaluated files to avoid duplicated evaluations.
Moved Updater class to dedicated header and source files. Created apiUpdater.js with a separate API for Updater component. Issues to fix: 1. Pass Prefs to Updater component - Now Updater sets just an empty Prefs object 2. Review content of apiUpdater.js and updaterJsFiles[] array - Check that all what's needed is set there and nothing is redundant - Does Updater component need to take care of asynchronicity in JS files? 3. Fix UpdateCheckTests - They are now passing just because FilterEngine object is created while those tests should just rely only on Updater object.
Added cpp methods for Prefs JS API. Removed not needed JS API. Added _isSubscriptionDownloadAllowed event callback to unblock downloads in JS code. Cleaned up UpdateCheckTest a bit removing FilterEngine because with the change above those tests don't require anymore FilterEnigne object to pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks pretty good, there are just some peculiarities.
include/AdblockPlus/JsEngine.h
Outdated
* Callback type for evaluating JS expression. | ||
* The parameter is the JS file name containing the expression. | ||
*/ | ||
typedef std::function<void(const std::string&)> EvaluateCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's actually unrelated to JsEngine
, it's rather an interface of FilterEngine
and Updater
, therefore IMO it would be better to define it there, thus two separate typedef
s in both FilterEngine
and Updater
.
src/FilterEngine.cpp
Outdated
#include "JsContext.h" | ||
#include "Thread.h" | ||
#include <mutex> | ||
#include <condition_variable> | ||
|
||
using namespace AdblockPlus; | ||
|
||
extern std::string jsSources[]; | ||
namespace { | ||
static std::string filterEngineJsFiles[] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not necessary to make it static
but it should be const
.
src/FilterEngine.cpp
Outdated
#include "JsContext.h" | ||
#include "Thread.h" | ||
#include <mutex> | ||
#include <condition_variable> | ||
|
||
using namespace AdblockPlus; | ||
|
||
extern std::string jsSources[]; | ||
namespace { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The coding style in this project implies that {
are located on a new line, could you please move it there?
src/FilterEngine.cpp
Outdated
// Set the preconfigured prefs | ||
auto preconfiguredPrefsObject = jsEngine->NewObject(); | ||
for (const auto& pref : params.preconfiguredPrefs) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seem unrelated, could you please revert it back?
src/FilterEngine.cpp
Outdated
@@ -228,7 +262,7 @@ void FilterEngine::CreateAsync(const JsEnginePtr& jsEngine, | |||
isSubscriptionDownloadAllowedCallback(params[0].IsString() ? &allowedConnectionType : nullptr, callJsCallback); | |||
}); | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seem unrelated, could you please revert it back?
src/Platform.cpp
Outdated
return; //NO-OP, file was already evaluated | ||
|
||
// Lock the JS engine while we are loading scripts | ||
const JsContext context(*jsEngine); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lock is not necessary here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check my response to your comment about removing "const JsContext context(*jsEngine);" from FilterEngine::CreateAsync().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My previous comment is not valid anymore.
src/Updater.cpp
Outdated
: jsEngine(jsEngine), updateCheckId(0) | ||
{ | ||
// Hack to enable downloads from JS (see compat.js) | ||
jsEngine->SetEventCallback("_isSubscriptionDownloadAllowed", [this](JsValueList&& params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code is unrelated to the Updater, could you please not bring it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, it is a bad hack. For the time being, to enable Updater downloads is JS code, that is to not rely on _isSubscriptionDownloadAllowed, I propose to add following execution path to send() method in compat.js just before _isSubscriptionDownloadAllowed() is executed:
if (this._url.includes("update.json"))
{
window._webRequest.GET(this._url, this._requestHeaders, onGetDone);
return;
}
Do you agree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, it's required. I agree, let's add that check to compat.js.
src/Updater.cpp
Outdated
|
||
// Set empty preconfigured prefs | ||
auto preconfiguredPrefsObject = jsEngine->NewObject(); | ||
jsEngine->SetGlobalProperty("_preconfiguredPrefs", preconfiguredPrefsObject); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is actually unrelated, however, since prefs.js is required and prefs.js expects that object, I would propose to change prefs.js. The latter should rather check whether _preconfiguredPrefs
exists. Additionally, it can happen that someone firstly instantiated Updater
and then is instantiating FilterEngine
, but prefs are already initialized. IMO we may not fully address it here but fix it later, under another ticket.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove this code from the Updater class and add a check to prefs.js to iterate through _preconfiguredPrefs only if this object exists. Is that enough for the scope of this task? Later on _preconfiguredPrefs could be split so FilterEngine and Updater would use dedicated objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's absolutely enough. I think later we should rather extract Prefs the similar way as Updater and make the former as a dependency for FilterEngine and Updater.
lib/apiUpdater.js
Outdated
|
||
let API_UPDATER = (() => | ||
{ | ||
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems Services
is not used here, could you please remove it?
include/AdblockPlus/Platform.h
Outdated
@@ -125,11 +133,15 @@ namespace AdblockPlus | |||
TimerPtr timer; | |||
FileSystemPtr fileSystem; | |||
WebRequestPtr webRequest; | |||
JsEngine::EvaluateCallback evaluateCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems unused, could you please remove it?
Thanks @abby-sergz, I've added my responses to clarify some things before I'll push new a commit which addresses review remarks. |
Apart from changes in new code, this also withdraws changes in existing code not related with this feature.
@abby-sergz , my commit from yesterday should address all review comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add a couple of tests instantiating both FilterEngine
and Updater
in different orders and I think it will be over.
@@ -91,6 +117,16 @@ FilterEngine& Platform::GetFilterEngine() | |||
return *std::shared_future<FilterEnginePtr>(filterEngine).get(); | |||
} | |||
|
|||
Updater& Platform::GetUpdater() | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to have creation of Updater
and evaluatedJsSources
to be thread safe, but perhaps it's actually fine for present because I suspect that all these creations will be re-engineered soon anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, it crossed my mind when I started writing this code. For the Updater thread safety, since Updater does not require async callback from JS code for creation, I propose to change constructor to:
Updater& Platform::GetUpdater()
{
{
std::lock_guard<std::mutex> lock(modulesMutex);
if (updater)
return *updater;
}
GetJsEngine(); // ensures that JsEngine is instantiated
{
std::lock_guard<std::mutex> lock(modulesMutex);
if (!updater)
updater = std::make_shared<Updater>(jsEngine, GetEvaluateCallback());
return *updater;
}
}
And I'll add a dedicated mutex to serialize access to evaluatedJsSources.
@abby-sergz , regarding requested tests do you want to check in those tests something specific to confirm that FilterEngine and Updater are operable, for example by calling Set(Get)Pref() on both of them? |
Good point, yeah, it would be great. Just call what you think makes sense.
пн, 6 авг. 2018 г. в 11:13, kzlomek <notifications@github.com>:
… @abby-sergz <https://github.com/abby-sergz> , regarding requested tests
do you want to check in those tests something specific to confirm that
FilterEngine and Updater are operable, for example by calling
Set(Get)Pref() on both of them?
—
|
7246e0a
to
98318ef
Compare
Now creation of Updater is thread safe. Added synchronization for evaluatedJsSources set. Added more unit tests.
98318ef
to
511c949
Compare
I've pushed commit "Fixed thread safety and added more tests" and then force pushed amendments in this commit with fixes in indentation and to resolve bug in the testes. I don't plan to update this commit so please review. |
|
||
static const size_t COUNT = 100; | ||
const std::string PROP_NAME = "patternsbackupinterval"; | ||
std::vector<std::thread> threadsVector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, one can use AdblockPlus::AsyncExecutor
from include/AdblockPlus/AsyncExecutor.h. It also should not be a member, just allocate it before the loop (below) and call asyncExecutor.Dispatch(the-same-labda)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll check it and use AsyncExecutor.
static const size_t COUNT = 100; | ||
const std::string PROP_NAME = "patternsbackupinterval"; | ||
std::vector<std::thread> threadsVector; | ||
Updater* updaterAddrArray[COUNT]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think about using of std::array here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good tip.
for (size_t idx = 0; idx < COUNT; ++idx) | ||
threadsVector.push_back( | ||
std::thread([this, idx, isUpdater, isFilterEngine]() -> void { | ||
Updater* updaterAddr = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need these variables? It seems one can just assign an item in the array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used them just to make the assignment in one place at the end of this method. But I can remove them.
TEST_F(UpdaterAndFilterEngineCreationTest, TestFilterEngineSingleInstance) | ||
{ | ||
CallGetFilterEngineSimultaneously(); | ||
FilterEngine* filterEngineAddr = filterAddrArray[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strictly speaking it would be good to test just in case that the value of the first element is not nullptr
since we expect it anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
TEST_F(UpdaterAndFilterEngineCreationTest, TestUpdaterAndFilterEngineCreationOrder1) | ||
{ | ||
Updater& updater = platform->GetUpdater(); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wonder, does it not work without that sleep?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, sleep is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's over, just a tiny thing is left there.
{ | ||
CallGetFilterEngineSimultaneously(); | ||
FilterEngine* filterEngineAddr = filterAddrArray[0]; | ||
ASSERT_NE(filterEngineAddr, nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not noticing it before. Even if it's a nullptr, we still can continue the execution of the test. It's a pretty subtle and perhaps even subjective thing, but could you please change all these ASSERT_* to EXPECT_*. It can seem inconsistent from other tests but we are gradually applying this approach.
Usually EXPECT_* are preferred, as they allow more than one failure to be reported in a test. However, you should use ASSERT_* if it doesn't make sense to continue when the assertion in question fails.
Therefore we usually use ASSERT_ in such cases when we know that the normal execution should not be possible, e.g. of a seg-fault because of dereferencing of a nullptr (in general case it can of course work without a seg-fault).
BTW, normally the order of arguments is that the expected value is going first and the actual value is the second one. Could you please change that order too if it does not cause compilation errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I know this appoach - I'll change those nullptr checks from ASSERT to EXPECT and will update the order, I'll push new commit in couple of minutes.
0db12e3
to
956d620
Compare
No description provided.